我使用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标记。
有没有办法告诉Jackson使用与XML和JSON不同的特定自定义反序列化器/序列化器?这是我认为可以解决的一种方式。
我的XML可能格式有点不同(我的形式并没有受到限制,但JSON必须保持这种格式)。有了这种灵活性,你有没有看到解决我的问题的替代方案?我可以使用与XML不同的东西,比如JAXB,但我几乎不能将Jackson用于两者。
答案 0 :(得分:2)
我有一个部分解决方案。使用Jackson mixin feature,可以为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;
}
接下来,您创建一个Jackson Module,将Data
类与mixin XmlData
类相关联,
@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();
}
}
答案 1 :(得分:0)
除了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>