2011-06-27

Gson v Jackson - Part 2

tl;dnr

Use Jackson, not Gson. Use this article as a reference for basic features.

Part 1 of this short series of articles ended with section 5.3 of the Gson user guide, titled "Nested Classes (including Inner Classes)". This second part continues with section 5.4 on "Array Examples", and ends with section 5.7 on "Serializing and Deserializing Collection with Objects of Arbitrary Types". Part 3 will continue with section 5.8 on "Built-in Serializers and Deserializers".
Link To This ArticleQR Code Link To This Articlehttp://goo.gl/qkceb

See part 6 of this series for a complete listing of and links to the various sections of this Gson user guide review.

For cross reference, readers will likely find it useful to also have the Gson user guide open in another browser window. (An archive of the Gson user guide as of 2011.06.26 is available at https://sites.google.com/site/programmerbruce/downloads/Gson_User_Guide_2011.06.26.zip.)

This information is based on Gson release 1.7.1 and Jackson release 1.8.2.

The Gson User Guide Walk-through Continued...


Array Examples


The Gson Code: See relevant section in the Gson user guide.

The comparable Jackson Code:
ObjectMapper mapper = new ObjectMapper();
int[] ints = {1, 2, 3, 4, 5};
String[] strings = {"abc", "def", "ghi"};

// (Serialization)
System.out.println(mapper.writeValueAsString(ints));
// output: [1,2,3,4,5]
System.out.println(mapper.writeValueAsString(strings));
// output: ["abc","def","ghi"]

// (Deserialization)
int[] ints2 = mapper.readValue("[1,2,3,4,5]", int[].class);
System.out.println(Arrays.toString(ints2));
// output: [1, 2, 3, 4, 5]
Comparison Rating: COMPARABLE

Additional Section Notes: This Gson user guide section ends with the statement, "We also support multi-dimensional arrays, with arbitrarily complex element types." This is unfortunately not an entirely correct statement, if it is applied to the context of data-binding, in which it was presented. The statement should be modified to read, "We also provide limited support for multi-dimensional arrays, with limited support for arbitrarily complex element types."

Examples to support this include the following.
Gson gson = new Gson();
ObjectMapper mapper = new ObjectMapper();

// 1-dimension array with varying primitive type elements
// input: ["abc",42]
String json1 = "[\"abc\",42]";

Object[] things1a = gson.fromJson(json1, Object[].class);
System.out.println(Arrays.toString(things1a));
// output: [abc, 42]

Object[] things1b = mapper.readValue(json1, Object[].class);
System.out.println(Arrays.toString(things1b));
// output: [abc, 42]

// 2x2 array with varying primitive type elements
// input: [["abc",42],["def",43]]
String json2 = "[[\"abc\",42],[\"def\",43]]";

Object[][] things2a1 = gson.fromJson(json2, Object[][].class);
System.out.println(Arrays.toString(things2a1[0]));
// output: [abc, 42]
System.out.println(Arrays.toString(things2a1[1]));
// output: [def, 43]

// throws JsonParseException, complaining:
// Type information is unavailable,
// and the target is not a primitive: ["abc",42]
// Object[] things2a2 = gson.fromJson(json2, Object[].class);

// throws JsonParseException, complaining:
// Type information is unavailable,
// and the target is not a primitive: [["abc",42],["def",43]]
// Object things2a3 = gson.fromJson(json2, Object.class);

Object[][] things2b1 =
mapper.readValue(json2, Object[][].class);
System.out.println(Arrays.toString(things2b1[0]));
// output: [abc, 42]
System.out.println(Arrays.toString(things2b1[1]));
// output: [def, 43]

Object[] things2b2 = mapper.readValue(json2, Object[].class);
System.out.println(Arrays.toString(things2b2));
// output: [[abc, 42], [def, 43]]
System.out.println(things2b2[0].getClass());
// output: class java.util.ArrayList

Object things2b3 = mapper.readValue(json2, Object.class);
System.out.println(things2b3);
// output: [[abc, 42], [def, 43]]
System.out.println(things2b3.getClass());
// output: class java.util.ArrayList

// "uneven" multi-dimensional array
// input: [["abc",[42,24]],["def",[43,34]]]
String json3 = "[[\"abc\",[42,24]],[\"def\",[43,34]]]";

// throws JsonParseException, complaining:
// Type information is unavailable,
// and the target is not a primitive: [42,24]
// Object[][] things3a1 =
// gson.fromJson(json3, Object[][].class);

// throws JsonParseException, complaining:
// Type information is unavailable,
// and the target is not a primitive: ["abc",[42,24]]
// Object[] things3a2 = gson.fromJson(json3, Object[].class);

// throws JsonParseException, complaining:
// Type information is unavailable,
// and the target is not a primitive:
// [["abc",[42,24]],["def",[43,34]]]
// Object things3a3 = gson.fromJson(json3, Object.class);

// throws RuntimeException
// Object[][][] things3a4 =
// gson.fromJson(json3, Object[][][].class);

Object[][] things3b1 =
mapper.readValue(json3, Object[][].class);
System.out.println(things3b1[0][0].getClass());
// output: class java.lang.String
System.out.println(things3b1[0][1].getClass());
// output: class java.util.ArrayList
System.out.println(Arrays.toString(things3b1[0]));
// output: [abc, [42, 24]]
System.out.println(Arrays.toString(things3b1[1]));
// output: [def, [43, 34]]

Object[] things3b2 = mapper.readValue(json3, Object[].class);
System.out.println(Arrays.toString(things3b2));
// output: [[abc, [42, 24]], [def, [43, 34]]]

Object things3b3 = mapper.readValue(json3, Object.class);
System.out.println(things3b3);
// [[abc, [42, 24]], [def, [43, 34]]]

mapper.configure(
DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY,
true);
Object[][][] things3b4 =
mapper.readValue(json3, Object[][][].class);
System.out.println(things3b4.length); // 2
System.out.println(things3b4[0].length); // 2
System.out.println(things3b4[0][0].length); // 1
System.out.println(things3b4[0][1].length); // 2
System.out.println(things3b4[1].length); // 2
System.out.println(things3b4[1][0].length); // 1
System.out.println(things3b4[1][1].length); // 2
System.out.println(things3b4[0][0][0].getClass());
// output: class java.lang.String
System.out.println(things3b4[0][1][0].getClass());
// output: class java.lang.Integer
System.out.println(things3b4[0][1][1].getClass());
// output: class java.lang.Integer
for (int i = 0; i < things3b4.length; i++)
for (int ii = 0; ii < things3b4[i].length; ii++)
for (int iii = 0; iii < things3b4[i][ii].length; iii++)
System.out.print(things3b4[i][ii][iii] + " ");
// output: abc 42 24 def 43 34

System.out.println();
mapper = new ObjectMapper();

// input: [42,{"one":1}]
String json4 = "[42,{\"one\":1}]";

// throws JsonParseException, complaining:
// Type information is unavailable,
// and the target is not a primitive: [42,{"one":1}]
// Object things4a1 = gson.fromJson(json4, Object.class);

// throws JsonParseException, complaining:
// Type information is unavailable,
// and the target object is not a primitive: {"one":1}
// Object[] things4a2 = gson.fromJson(json4, Object[].class);

Object things4b1 = mapper.readValue(json4, Object.class);
System.out.println(things4b1);
// output: [42, {one=1}]
System.out.println(things4b1.getClass());
// output: class java.util.ArrayList
List things4b1List = (List) things4b1;
System.out.println(things4b1List.size()); // 2
System.out.println(things4b1List.get(0).getClass());
// output: class java.lang.Integer
System.out.println(things4b1List.get(1).getClass());
// output: class java.util.LinkedHashMap

Object[] things4b2 = mapper.readValue(json4, Object[].class);
System.out.println(Arrays.toString(things4b2));
// output: [42, {one=1}]
System.out.println(things4b2[0].getClass());
// class java.lang.Integer
System.out.println(things4b2[1].getClass());
// class java.util.LinkedHashMap
Additional Code Notes:
Comparison Ratings:
  • +1 Jackson for significantly more complete data-binding support of multi-dimensional arrays
  • +1 Jackson for significantly more complete data-binding support of arbitrarily complex array element types

Collections Examples


The Gson Code: See relevant section in the Gson user guide.

The comparable Jackson Code:
ObjectMapper mapper = new ObjectMapper();
Collection<Integer> ints = Arrays.asList(1,2,3,4,5);

// (Serialization)
String json = mapper.writeValueAsString(ints);
System.out.println(json); // [1,2,3,4,5]

// (Deserialization)
TypeReference collectionType =
new TypeReference<Collection<Integer>>(){};
Collection<Integer> ints2 =
mapper.readValue(json, collectionType);
System.out.println(ints2); // [1, 2, 3, 4, 5]

CollectionType collectionType2 =
TypeFactory.defaultInstance().constructCollectionType(
Collection.class, Integer.class);
Collection<Integer> ints3 =
mapper.readValue(json, collectionType2);
System.out.println(ints3); // [1, 2, 3, 4, 5]
Code Notes: As demonstrated, Jackson offers two ways to solve the deserialization to generic types problem.

Comparison Rating: COMPARABLE

Collections Limitations


Concerning the Gson user guide statements on collections limitations, including that Gson "[c]an serialize [a] collection of arbitrary objects but can not deserialize from it," note that Jackson is not similarly deficient, provided that polymorphic type information is available in the JSON and that other necessary configurations are made. For examples of deserializing JSON into polymorphic structures with Jackson, see http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html.

Comparison Rating: +1 Jackson for built-in polymorphic deserialization capability****

****The next release of Gson is planned to included some built-in support for polymorphic deserialization, as described in issue 231.

Serializing and Deserializing Generic Types


The Gson Code: See relevant section in the Gson user guide.

Note that the guide's statement that gson.toJson(myStrings); will cause a runtime exception is wrong, depending on the actual type of the list instance. The following demonstrates this point using an ArrayList.
List<String> myStrings = new ArrayList<String>();
myStrings.add("one");
myStrings.add("two");
myStrings.add("three");

Gson gson = new Gson();

String json1 = gson.toJson(myStrings);
System.out.println(json1);
// output: ["one","two","three"]

List<String> myStrings2 =
gson.fromJson(json1, myStrings.getClass());
System.out.println(myStrings2);
// output: [java.lang.Object@27ce2dd4,
// java.lang.Object@5122cdb6, java.lang.Object@43ef9157]
Object o1 = myStrings2.get(0);
System.out.println(o1.getClass());
// class java.lang.Object

ObjectMapper mapper = new ObjectMapper();

String json2 = mapper.writeValueAsString(myStrings);
System.out.println(json2);
// output: ["one","two","three"]

List<String> myStrings3 =
mapper.readValue(json2, myStrings.getClass());
System.out.println(myStrings3);
// output: [one, two, three]
Object o2 = myStrings3.get(0);
System.out.println(o2.getClass());
// output: class java.lang.String
Additional Notes:
  • Note that Gson populated the deserialized list with useless Object instances, retaining none of the data from the JSON input. This is a problem that Gson also has with non-generic Map collections. (See issue 325 for details.)
  • The use of a TypeToken to resolve this issue, and the Jackson counter parts, were demonstrated in the Collections Examples section.

Comparison Rating: +1 Jackson for significantly better support of deserializing to non-generic collections

Serializing and Deserializing Collection with Objects of Arbitrary Types


The Gson Code: See relevant section in the Gson user guide.

Differing from Gson, Jackson can deserialize the example JSON to a collection, without generic type information, and without implementing any of the suggested custom processing solutions.

The Jackson Code:
ObjectMapper mapper = new ObjectMapper();

Collection collection = new ArrayList();
collection.add("hello");
collection.add(5);
collection.add(new Event("GREETINGS", "guest"));

System.out.println(mapper.writeValueAsString(collection));
// output: ["hello",5,{"name":"GREETINGS","source":"guest"}]

// input: ["hello",5,{"name":"GREETINGS","source":"guest"}]
String json =
"[\"hello\",5,{\"name\":\"GREETINGS\",\"source\":\"guest\"}]";

Collection things = mapper.readValue(json, Collection.class);
System.out.println(things);
// output: [hello, 5, {name=GREETINGS, source=guest}]
Comparison Ratings:
  • COMPARABLE for similar support to serialize collections with arbitrary types
  • +1 Jackson for significantly better support of deserializing to non-generic collections

Continue to part 3...

References And Resources:

No comments:

Post a Comment