[英]Jackson Serialization: Different formats for XML and JSON
我使用 Jackson 将我的应用程序模型序列化/反序列化为 JSON 和 XML(两者都需要)。
模型类:
@JacksonXmlRootElement
public class Data {
@JsonProperty("attributes")
@JsonDeserialize(using = AttributesDeserializer.class)
@JsonSerialize(using = AttributesSerializer.class)
@JacksonXmlElementWrapper
private Map<Key, Map<String, Attribute>> attributes;
....
public class Key {
private Integer id;
private String name;
....
public class Attribute {
private Integer id;
private Integer value;
private String name;
我需要我的 JSON 看起来像这样:
{
"attributes": [
{
"key": {
"id": 10,
"name": "key1"
},
"value": {
"numeric": {
"id": 1,
"value": 100,
"name": "numericAttribute"
},
"text": {
"id": 2,
"value": 200,
"name": "textAttribute"
}
}
},
{
"key": {
"id": 20,
"name": "key2"
},
"value": {
"numeric": {
"id": 1,
"value": 100,
"name": "numericAttribute"
},
"text": {
"id": 2,
"value": 200,
"name": "textAttribute"
}
}
}
]
}
我的 XML 是这样的:
<Data>
<attributes>
<key>
<id>10</id>
<name>key1</name>
</key>
<value>
<numeric>
<id>1</id>
<value>100</value>
<name>numericAttribute</name>
</numeric>
<text>
<id>2</id>
<value>200</value>
<name>textAttribute</name>
</text>
</value>
<key>
<id>20</id>
<name>key2</name>
</key>
<value>
<numeric>
<id>1</id>
<value>100</value>
<name>numericAttribute</name>
</numeric>
<text>
<id>2</id>
<value>200</value>
<name>textAttribute</name>
</text>
</value>
</attributes>
</Data>
我正在使用自定义序列化程序获得所需的 JSON 和 XML:
public class AttributesSerializer extends JsonSerializer<Map<Key, Map<String, Attribute>>> {
@Override
public void serialize(Map<Key, Map<String, Attribute>> map, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeStartArray();
for (Map.Entry<Key, Map<String, Attribute>> entry : map.entrySet()) {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField("key", entry.getKey());
jsonGenerator.writeObjectFieldStart("value");
for (Map.Entry<String, Attribute> attributesEntry : entry.getValue().entrySet()) {
jsonGenerator.writeObjectField(attributesEntry.getKey(), attributesEntry.getValue());
}
jsonGenerator.writeEndObject();
jsonGenerator.writeEndObject();
}
jsonGenerator.writeEndArray();
}
}
使用自定义反序列化器,反序列化对 JSON 效果很好:
public class AttributesDeserializer extends JsonDeserializer<Map<Key, Map<String, Attribute>>> {
@Override
public Map<Key, Map<String, Attribute>> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
if (node.size() == 0) {
return null;
}
ObjectMapper om = new ObjectMapper();
Map<Key, Map<String, Attribute>> attributes = new HashMap<>();
node.forEach(jsonNode -> {
Map<String, Attribute> attributesMap = new HashMap<>();
JsonNode keyNode = jsonNode.get("key");
Key key = om.convertValue(keyNode, Key.class);
JsonNode valueNode = jsonNode.get("value");
Iterator<Map.Entry<String, JsonNode>> attributesIterator = valueNode.fields();
while(attributesIterator.hasNext()) {
Map.Entry<String, JsonNode> field = attributesIterator.next();
Attribute attribute = om.convertValue(field.getValue(), Attribute.class);
attributesMap.put(field.getKey(), attribute);
}
attributes.put(key, attributesMap);
});
return attributes;
}
}
虽然对于 JSON 来说一切都很好,但对于 XML,应用程序在反序列化中崩溃:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: ro.alexsvecencu.jackson.Data["attributes"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:388)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:348)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1599)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:278)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2740)
at ro.alexsvecencu.jackson.Main.main(Main.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.NullPointerException
at ro.alexsvecencu.jackson.AttributesDeserializer.lambda$deserialize$0(AttributesDeserializer.java:29)
at ro.alexsvecencu.jackson.AttributesDeserializer$$Lambda$1/1709366259.accept(Unknown Source)
at java.lang.Iterable.forEach(Iterable.java:75)
at ro.alexsvecencu.jackson.AttributesDeserializer.deserialize(AttributesDeserializer.java:24)
at ro.alexsvecencu.jackson.AttributesDeserializer.deserialize(AttributesDeserializer.java:15)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:499)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:101)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
... 9 more
发生的事情是我的自定义反序列化器为 XML 崩溃,因为显然它没有将所有属性解释为“数组”,并且当我遍历 jsonNode 的子节点时,它将遍历键/值。 此外,通过调试,我注意到反序列化器只是为 XML 中属性的 LAST 标记调用。
有没有办法告诉杰克逊使用 XML 和 JSON 不同的特定自定义反序列化器/序列化器? 这是我认为可以解决的一种方法。
我的 XML 格式可能有点不同(我并没有真正限制它的形式,但 JSON 必须保持这种格式)。 有了这种灵活性,您有没有其他方法可以解决我的问题? 我可以对 XML 使用不同的东西,比如 JAXB,但我几乎受限于两者都使用 Jackson。
我有一个部分解决方案。 使用Jackson mixin功能 ,可以为XML和JSON提供不同的自定义反序列化器/序列化器
首先,创建另一个POJO类,该类具有与 Data
类同名的属性,并具有自定义反序列化器/序列化器的不同注释
@JacksonXmlRootElement
public static class XmlData
{
@JsonProperty("attributes")
@JsonDeserialize(using = XmlAttributesDeserializer.class) // specify different serializer
@JsonSerialize(using = XmlAttributesSerializer.class) // specify different deserializer
@JacksonXmlElementWrapper
public Map<Key, Map<String, Attribute>> attributes;
}
接下来,创建一个将Data
类与mixin XmlData
类相关联的Jackson模块 ,
@SuppressWarnings("serial")
public static class XmlModule extends SimpleModule
{
public XmlModule()
{
super("XmlModule");
}
@Override
public void setupModule(SetupContext context)
{
context.setMixInAnnotations(Data.class, XmlData.class);
}
}
这是一个测试方法,显示如何将模块注册到映射器并动态序列化为不同的格式:
public static void main(String[] args)
{
Attribute a1 = new Attribute();
a1.id = 1;
a1.value = 100;
a1.name = "numericAttribute";
Attribute a2 = new Attribute();
a2.id = 2;
a2.value = 200;
a2.name = "textAttribute";
Map<String, Attribute> atts = new HashMap<>();
atts.put("numeric", a1);
atts.put("text", a2);
Key k1 = new Key();
k1.id = 10;
k1.name = "key1";
Key k2 = new Key();
k2.id = 20;
k2.name = "key2";
Data data = new Data();
data.attributes = new HashMap<>();
data.attributes.put(k1, atts);
data.attributes.put(k2, atts);
ObjectMapper mapper;
if ("xml".equals(args[0])) {
mapper = new XmlMapper();
mapper.registerModule(new XmlModule());
} else {
mapper = new ObjectMapper();
}
try {
mapper.writeValue(System.out, data);
} catch (Exception e) {
e.printStackTrace();
}
}
除了sharonbn提供的解决方案之外 ,我发现如果你可以将你的字段封装到另一个类中,你可以通过一个模块为Json(ObjectMapper)或Xml(XmlMapper)注册一个不同的序列化器。
这是一个实例:
public class CustomSerializers {
public static class PojoField {
PojoField() {
value = "PojoField";
}
public String value;
}
@JacksonXmlRootElement
public static class Pojo {
Pojo() {
field = new PojoField();
}
@JsonProperty("field")
public PojoField field;
}
public static class PojoFieldJSonSerializer extends JsonSerializer<PojoField> {
@Override
public void serialize(PojoField pojoField, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeObject(pojoField.value + " in JSON");
}
}
public static class PojoFieldXmlSerializer extends JsonSerializer<PojoField> {
@Override
public void serialize(PojoField pojoField, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeObject(pojoField.value + " in XMl");
}
}
public static void main(String []args) throws IOException {
Pojo pojo = new Pojo();
SimpleModule objectMapperModule = new SimpleModule();
objectMapperModule.addSerializer(PojoField.class, new PojoFieldJSonSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(objectMapperModule);
objectMapper.writeValue(new File("pojo.json"), pojo);
SimpleModule xmlMapperModule = new SimpleModule();
xmlMapperModule.addSerializer(PojoField.class, new PojoFieldXmlSerializer());
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(xmlMapperModule);
xmlMapper.writeValue(new File("pojo.xml"), pojo);
}
}
JSON的输出将是:
{"field": "PojoField in JSON"}
XML的输出:
<Pojo>
<field>PojoField in XMl</field>
</Pojo>
对 Json 和 Xml 都使用唯一的 Serializer/Derializer 并在其中检查 JsonGenerator 的类型(json 或 xml)并应用与特定格式相关的逻辑是否是解决方案? 像那样
public class AttributeSerializer extends JsonSerializer<Map<Key, Map<String, Attribute>>> {
@Override
public void serialize(Map<Key, Map<String, Attribute>> value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
if (gen instanceof ToXmlGenerator) {
//apply logic to format xml structure
} else {
//apply logic to format json or else
}
}
}
我知道使用instaceOf
不是一个好的架构,但是这样我们可以避免 dto 对 xml 的重复。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.