如何为父项和子项 类 使用单独的自定义 json 序列化程序?
How to use separate custom json serializers for parent and child classes?
在下面的示例中,我有一个主 class - A 及其子 class - B:
public class A
{
public final String primaryKey;
A(String primaryKey)
{
this.primaryKey = primaryKey;
}
}
public class B extends A
{
public final String secondaryKey;
B(String primaryKey, String secondaryKey)
{
super(primaryKey);
this.secondaryKey = secondaryKey;
}
}
我还有自定义序列化程序:
public class ASerializer extends StdSerializer<A> {
public ASerializer(Class<A> t)
super(t);
}
@Override
public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) {
try {
jgen.writeStartObject();
jgen.writeFieldName("base");
jgen.writeStartObject();
jgen.writeStringField("CustomKeyA", value.primaryKey);
jgen.writeEndObject();
jgen.writeEndObject();
} catch (IOException e) {
e.printStackTrace();
}
}
}
因此,以下代码使用自定义序列化器进行 json 序列化:
ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("testModule", Version.unknownVersion());
testModule.addSerializer(A.class, new ASerializer(A.class));
result = mapper.writeValueAsString(new B("A", "B"));
System.out.println(result);
实际输出:
{
"base" : {
"CustomKeyA":"A"
}
}
扩展输出:
{
"base" : {
"CustomKeyA":"A"
},
"secondaryKey":"B"
}
那么,如何以自定义方式序列化一个超级 class 成员并以标准方式序列化所有其他子 class 成员?谢谢。
UPD:更正。
如果您的用例仅限于为 class A
的字段定义特定名称,那么您 不需要 来实现和注册自定义序列化程序。通过添加
可以实现所需的输出
@JsonProperty("CustomKeyA")
public final String primaryKey;
primaryKey
和 secondaryKey
属性都将由 Jackson 处理 class B
并包含在输出中作为 CustomKeyA
和 secondaryKey
分别。
使用自定义序列化程序可以完全控制要输出的元素,绕过默认序列化机制:这意味着必须处理属性 "manually",这很容易变得复杂 class 层次结构。
更新
如果你不允许修改原来的classes A
或B
,仍然可以使用序列化器work-around。
该方法的主要困难在于父 class A
的序列化程序不太了解它可能接收的从 A
继承的对象的属性。检查特定类型、转换和输出相应字段将非常困难。维护起来会特别地狱
我可以建议的解决方案的想法是:使用 Jackson 的机制将对象的属性检索为 Map
(以便所有可输出的属性都存在)并替换父对象之一class 使用适当的密钥。然后将地图输出成JSON.
可以这样实现:
public class ASerializer extends StdSerializer<A> {
// we'll use this mapper to convert the object into a Map
private static final ObjectMapper MAPPER = new ObjectMapper();
// contains the list of property names that belong to parent class only
private static final Set<String> BASE_PROPERTIES = new HashSet<>(Arrays.asList("primaryKey"));
public ASerializer() {
this(A.class);
}
public ASerializer(Class<A> t) {
super(t);
}
private void serializeBaseProperties(A value, JsonGenerator jgen) throws IOException {
// create a Map of base properties and their values to serialize under "base"
final Map<String, Object> baseProperties = new HashMap<>();
baseProperties.put("CustomKeyA", value.primaryKey);
// output the map
jgen.writeFieldName("base");
jgen.writeObject(baseProperties);
}
@SuppressWarnings("unchecked")
private void serializeOwnProperties(A value, JsonGenerator jgen) {
((Map<String, Object>) MAPPER.convertValue(value, Map.class)) // grab all serializable properties
.entrySet().stream()
.filter(entry -> !BASE_PROPERTIES.contains(entry.getKey())) // filter out the ones from base class
.forEach(property -> writeProperty(property, jgen)); // output own properties to JSON
}
@Override
public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
serializeBaseProperties(value, jgen);
serializeOwnProperties(value, jgen);
jgen.writeEndObject();
}
private void writeProperty(Map.Entry<String, Object> property, JsonGenerator jgen) {
try {
jgen.writeFieldName(property.getKey());
jgen.writeObject(property.getValue());
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
可能还有另一种方法,即创建与目标 classes 层次结构并行的序列化程序层次结构。这也可行,但同样会造成冗余并使其更难维护。
End-of-update
在下面的示例中,我有一个主 class - A 及其子 class - B:
public class A
{
public final String primaryKey;
A(String primaryKey)
{
this.primaryKey = primaryKey;
}
}
public class B extends A
{
public final String secondaryKey;
B(String primaryKey, String secondaryKey)
{
super(primaryKey);
this.secondaryKey = secondaryKey;
}
}
我还有自定义序列化程序:
public class ASerializer extends StdSerializer<A> {
public ASerializer(Class<A> t)
super(t);
}
@Override
public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) {
try {
jgen.writeStartObject();
jgen.writeFieldName("base");
jgen.writeStartObject();
jgen.writeStringField("CustomKeyA", value.primaryKey);
jgen.writeEndObject();
jgen.writeEndObject();
} catch (IOException e) {
e.printStackTrace();
}
}
}
因此,以下代码使用自定义序列化器进行 json 序列化:
ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("testModule", Version.unknownVersion());
testModule.addSerializer(A.class, new ASerializer(A.class));
result = mapper.writeValueAsString(new B("A", "B"));
System.out.println(result);
实际输出:
{
"base" : {
"CustomKeyA":"A"
}
}
扩展输出:
{
"base" : {
"CustomKeyA":"A"
},
"secondaryKey":"B"
}
那么,如何以自定义方式序列化一个超级 class 成员并以标准方式序列化所有其他子 class 成员?谢谢。
UPD:更正。
如果您的用例仅限于为 class A
的字段定义特定名称,那么您 不需要 来实现和注册自定义序列化程序。通过添加
@JsonProperty("CustomKeyA")
public final String primaryKey;
primaryKey
和 secondaryKey
属性都将由 Jackson 处理 class B
并包含在输出中作为 CustomKeyA
和 secondaryKey
分别。
使用自定义序列化程序可以完全控制要输出的元素,绕过默认序列化机制:这意味着必须处理属性 "manually",这很容易变得复杂 class 层次结构。
更新
如果你不允许修改原来的classes A
或B
,仍然可以使用序列化器work-around。
该方法的主要困难在于父 class A
的序列化程序不太了解它可能接收的从 A
继承的对象的属性。检查特定类型、转换和输出相应字段将非常困难。维护起来会特别地狱
我可以建议的解决方案的想法是:使用 Jackson 的机制将对象的属性检索为 Map
(以便所有可输出的属性都存在)并替换父对象之一class 使用适当的密钥。然后将地图输出成JSON.
可以这样实现:
public class ASerializer extends StdSerializer<A> {
// we'll use this mapper to convert the object into a Map
private static final ObjectMapper MAPPER = new ObjectMapper();
// contains the list of property names that belong to parent class only
private static final Set<String> BASE_PROPERTIES = new HashSet<>(Arrays.asList("primaryKey"));
public ASerializer() {
this(A.class);
}
public ASerializer(Class<A> t) {
super(t);
}
private void serializeBaseProperties(A value, JsonGenerator jgen) throws IOException {
// create a Map of base properties and their values to serialize under "base"
final Map<String, Object> baseProperties = new HashMap<>();
baseProperties.put("CustomKeyA", value.primaryKey);
// output the map
jgen.writeFieldName("base");
jgen.writeObject(baseProperties);
}
@SuppressWarnings("unchecked")
private void serializeOwnProperties(A value, JsonGenerator jgen) {
((Map<String, Object>) MAPPER.convertValue(value, Map.class)) // grab all serializable properties
.entrySet().stream()
.filter(entry -> !BASE_PROPERTIES.contains(entry.getKey())) // filter out the ones from base class
.forEach(property -> writeProperty(property, jgen)); // output own properties to JSON
}
@Override
public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeStartObject();
serializeBaseProperties(value, jgen);
serializeOwnProperties(value, jgen);
jgen.writeEndObject();
}
private void writeProperty(Map.Entry<String, Object> property, JsonGenerator jgen) {
try {
jgen.writeFieldName(property.getKey());
jgen.writeObject(property.getValue());
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
}
可能还有另一种方法,即创建与目标 classes 层次结构并行的序列化程序层次结构。这也可行,但同样会造成冗余并使其更难维护。
End-of-update