Json deserialization with Jackson and Super type tokens

Datatables is a jquery plugin to present tabular information – it can enhance a simple table or can use a AJAX based data and present the information in a tabular form.

Datatables requires the data from the server to follow a specific JSON format for it to be displayed on screen. Consider the case where a list of Member entities is to be displayed, the expected json structure for Members then has to be along these lines:
 
 
 
 
 
 

{
   'aaData':[
      {
         'id':1,
         'first':'one',
         'last':'one',
         'addresses':[

         ],
         'version':0
      },
      {
         'id':2,
         'first':'two',
         'last':'two',
         'addresses':[

         ],
         'version':0
      }
   ],
   'iTotalRecords':100,
   'iTotalDisplayRecords':10,
   'success':true
}

A generic java type can be defined which Jackson can use to generate json of the type shown above, consider the following Java generic type:

package mvcsample.types;
import java.util.List;

public class ListWrapper<T> {
    private List<T> aaData;
    private int iTotalRecords;
    private int iTotalDisplayRecords;
    private  Boolean success;

    public List<T> getAaData() {
  return aaData;
 }
 public void setAaData(List<T> aaData) {
  this.aaData = aaData;
 }
 public int getiTotalRecords() {
  return iTotalRecords;
 }
 public void setiTotalRecords(int iTotalRecords) {
  this.iTotalRecords = iTotalRecords;
 }
 public int getiTotalDisplayRecords() {
  return iTotalDisplayRecords;
 }
 public void setiTotalDisplayRecords(int iTotalDisplayRecords) {
  this.iTotalDisplayRecords = iTotalDisplayRecords;
 }
 public Boolean getSuccess() {
  return success;
 }
 public void setSuccess(Boolean success) {
  this.success = success;
 }   
}

So, with this generic type, to generate a list of Members I would have a parameterized type defined as in this test:

List<Member> members = new ArrayList<>();
members.add(new Member('one', 'one'));
members.add(new Member('two', 'two'));
ListWrapper<Member> membersWrapper = new ListWrapper<>();
membersWrapper.setAaData(members);
membersWrapper.setiTotalDisplayRecords(10);
membersWrapper.setiTotalRecords(100);
ObjectMapper objectMapper = new ObjectMapper();

StringWriter w = new StringWriter();
objectMapper.writeValue(w, membersWrapper);
String json = w.toString();
System.out.println(json);

And similarly a json for any other type can be generated.

However, what about the other way around, generating the Java type given the json.

Again, consider a case where the json given in the beginning is to be converted to ListWrapper<Member> , I can try a deserialization this way:

ObjectMapper objectMapper = new ObjectMapper();  
ListWrapper<Member> membersUpdated = objectMapper.readValue(json, ListWrapper.class);

Note that above I cannot refer to the class type as ListWrapper<Member>.class, I can only refer to it as ListWrapper.class.

This however will not work and the resulting type will not be a wrapper around Member class, as at runtime Jackson has no idea that it has to generate a ListWrapper<Member>.

The fix is to somehow pass the information about the ListWrapper’s type to Jackson and this is where Super type tokens fits in. The article explains how this works in great detail, the essence is that while type erasure does remove the type information from parameterized instances of generic type, however the type is retained in subclasses of generic classes.

For eg. Consider the following StringList class which derives from ArrayList<String> , it is possible to find that the type parameter of the base class is a String as shown in the test below:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;

public class StringList extends ArrayList<String>{

 public static void main(String[] args) {
  StringList list = new StringList();
  Type superClassType = list.getClass().getGenericSuperclass();
  ParameterizedType parameterizedType = (ParameterizedType)superClassType;
  System.out.println(parameterizedType.getActualTypeArguments()[0]);
 }
}

This is applicable for a case where a subclass is defined as an anonymous class also this way:

ArrayList<String> list = new ArrayList<String>(){};
Type superClassType = list.getClass().getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType)superClassType;
System.out.println(parameterizedType.getActualTypeArguments()[0]);

This is what is used internally with the Super Type tokens pattern to find the type of the parameterized type. Jackson’s com.fasterxml.jackson.core.type.TypeReference abstract class implements this and using this the Jackson deserialization would work this way:

import com.fasterxml.jackson.core.type.TypeReference;

....
ListWrapper<Member> membersWrapper = objectMapper.readValue(json, new TypeReference<ListWrapper<Member>>() {});

ListWrapper<Address> addressWrapper = objectMapper.readValue(json, new TypeReference<ListWrapper<Address>>() {});

This way two different parameterized types can be deserialized given a generic type and a json representation.

Resources:

 

Reference: Json deserialization with Jackson and Super type tokens from our JCG partner Biju Kunjummen at the all and sundry blog.

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

One Response to "Json deserialization with Jackson and Super type tokens"

  1. Very useful. Another way to create type values which works even in dynamic cases (i.e. ones where caller only gets a Class) is to use TypeFactory: it can be used to construct generic types programmatically:

    JavaType type = mapper.getTypeFactory().constructParametricType(ListWrapper.class, valueClass);

    and pass that as the type.

Leave a Reply


9 − seven =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books