I'm trying to convert xml into json using jackson-2.5.1 and jackson-dataformat-xml-2.5.1
The xml structure is received from web server and unknown, therefore I can't have java class to represent the object, and I'm trying to convert directly into TreeNode using ObjectMapper.readTree.
My problem is jackson failing to parse lists. It is takes only the last item of the list.
code:
String xml = "<root><name>john</name><list><item>val1</item>val2<item>val3</item></list></root>";
XmlMapper xmlMapper = new XmlMapper();
JsonNode jsonResult = xmlMapper.readTree(xml);
The json result:
{"name":"john","list":{"item":"val3"}}
If I enable failure on duplicate keys xmlMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY), exception is thrown:com.fasterxml.jackson.databind.JsonMappingException: Duplicate field 'item' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled
Is there any feature which fixes this problem? Is there a way for me to write custom deserializer which in event of duplicate keys turn them into array?
I use this approach:
size==1.Here is my code:
@Test
public void xmlToJson() {
String xml = "<root><name>john</name><list><item>val1</item>val2<item>val3</item></list></root>";
Map<String, Object> jsonResult = readXmlToMap(xml);
String jsonString = toString(jsonResult);
System.out.println(jsonString);
}
private Map<String, Object> readXmlToMap(String xml) {
try {
ObjectMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new SimpleModule().addDeserializer(Object.class, new UntypedObjectDeserializer() {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
protected Map<String, Object> mapObject(JsonParser jp, DeserializationContext ctxt) throws IOException {
JsonToken t = jp.getCurrentToken();
Multimap<String, Object> result = ArrayListMultimap.create();
if (t == JsonToken.START_OBJECT) {
t = jp.nextToken();
}
if (t == JsonToken.END_OBJECT) {
return (Map) result.asMap();
}
do {
String fieldName = jp.getCurrentName();
jp.nextToken();
result.put(fieldName, deserialize(jp, ctxt));
} while (jp.nextToken() != JsonToken.END_OBJECT);
return (Map) result.asMap();
}
}));
return (Map) xmlMapper.readValue(xml, Object.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static public String toString(Object obj) {
try {
ObjectMapper jsonMapper = new ObjectMapper().configure(SerializationFeature.INDENT_OUTPUT, true)
.configure(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED, true);
StringWriter w = new StringWriter();
jsonMapper.writeValue(w, obj);
return w.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
It prints
{
"list" : {
"item" : [ "val1", "val3" ]
},
"name" : "john"
}
Altogether it is a variante of this approach, which comes out without guava multimap: https://github.com/DinoChiesa/deserialize-xml-arrays-jackson
Same approach is used here: Jackson: XML to Map with List deserialization
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With