Jackson for XML – JSON conversion

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;
 }
 }
 
 }





Advertisements

One thought on “Jackson for XML – JSON conversion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s