WebLink : http://jackson.codehaus.org/
Dependencies:
- No External Dependencies.
- Required components:Jackson-annotation, Jackson-core,Jackson-databind,Jackson-dataformat-xml. Jackson-dataformat-xml needs to be brought to Platform. All other components are already available in Platform.
License: The Apache Software License, Version 2.0
Advantages:
- Faster.
- Already being used in platform
- actively developed library.
Disadvantages:
Example code:
xmlToJson |
public static void testXMLToJSON(String xml) throws JsonParseException, JsonMappingException, IOException{ ObjectMapper mapper = new ObjectMapper(); XmlMapper xmlMapper = new XmlMapper(); xmlMapper.setConfig(xmlMapper.getSerializationConfig().withRootName("")); Map<Object,Object> map = xmlMapper.readValue(xml, Map.class); String json = mapper.writeValueAsString(map); System.out.println(json); } |
JsonToXml |
public static void testJSONtoXML(String json) throws JsonParseException, JsonMappingException, IOException{ ObjectMapper mapper = new ObjectMapper(); Map<Object,Object> map = mapper.readValue(json, Map.class); XmlMapper xmlMapper = new XmlMapper(); String xml = xmlMapper.writeValueAsString(map); System.out.println(xml); } |
There is an issue with using java.util.Map as the java object to map xml content. It doesn’t give expected result when there is multiple elements with same tag name. This can be seen from the below samples(InputXml,ExpectedJson and ActualJson).
InputXml |
<user> <name>John</name> <age>21</age> <data>1</data> <data>2</data> <data>3</data> <data>4</data> </user> |
Consider the above xml, when it is converted to json, it should be converted as given in ExpectedJson.
ExpectedJson |
{ "user":{ "name":"John", "age":21, "data":[ 1, 2, 3, 4 ] } } |
ActualJson |
{ "user":{ "name":"John", "age":"21", "data":"4" } } |
This can be overcome by customizing the Deserialization behaviour. Basically when there is multiple value for the same key in a Map, we need to convert the value as an array.
public static void testXMLToJSON(String xml) throws JsonParseException, JsonMappingException, IOException { System.out.println(xml); JacksonXmlModule module = new JacksonXmlModule(); module.setDefaultUseWrapper(false); XmlMapper xmlMapper = new XmlMapper(); xmlMapper.registerModule(module); ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); Object map = xmlMapper.readValue(xml, GenericObject.class); String json = mapper.writeValueAsString(map); System.out.println(json); } @JsonDeserialize(using = CustomDeserializer.class) public class GenericObject { } public static class CustomDeserializer extends UntypedObjectDeserializer { private static final long serialVersionUID = -4628994110702279382L; protected Object mapObject(JsonParser jp, DeserializationContext ctxt) throws IOException { JsonToken t = jp.getCurrentToken(); if (t == JsonToken.START_OBJECT) { t = jp.nextToken(); } // minor optimization; let's handle 1 and 2 entry cases separately if (t == JsonToken.END_OBJECT) { // and empty one too // empty map might work; but caller may want to modify... so better just give small modifiable return new LinkedHashMap<String, Object>(2); } String field1 = jp.getCurrentName(); jp.nextToken(); Object value1 = deserialize(jp, ctxt); if (jp.nextToken() == JsonToken.END_OBJECT) { // single entry; but we want modifiable LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(2); value1 = handleMaultipleValue(result, field1, value1); result.put(field1, value1); return result; } String field2 = jp.getCurrentName(); jp.nextToken(); Object value2 = deserialize(jp, ctxt); if (jp.nextToken() == JsonToken.END_OBJECT) { LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(4); value1 = handleMaultipleValue(result, field1, value1); result.put(field1, value1); value2 = handleMaultipleValue(result, field2, value2); result.put(field2, value2); return result; } // And then the general case; default map size is 16 LinkedHashMap<String, Object> result = new LinkedHashMap<String, Object>(); value1 = handleMaultipleValue(result, field1, value1); result.put(field1, value1); value2 = handleMaultipleValue(result, field2, value2); result.put(field2, value2); do { String fieldName = jp.getCurrentName(); jp.nextToken(); Object value = deserialize(jp, ctxt); value = handleMaultipleValue(result, fieldName, value); result.put(fieldName, value); } while (jp.nextToken() != JsonToken.END_OBJECT); return result; } @SuppressWarnings("unchecked") private Object handleMaultipleValue(Map<String, Object> map, String key, Object value) { if (!map.containsKey(key)) { return value; } Object originalValue = map.get(key); if (originalValue instanceof List) { ((List) originalValue).add(value); return originalValue; } else { ArrayList newValue = new ArrayList(); newValue.add(originalValue); newValue.add(value); return newValue; } } } |
[…] Hat tip to Jegan for the custom deserializer. […]
LikeLike