Gson Advanced — Custom Instance Creator

In this blog post, we'll look at one more component when it comes to custom deserialization. In the past few blog posts, we've explored how to customize the serialization and deserialization of your data. In both cases we're trying to mitigate differences in the data models between server and client.

In this blog post we'll look at the case where both models are identical, but the client has additional (helper) properties or particular constructors that need to be called when deserializing data.

Of course, this will not be the only post in our Gson series. If you're interested in the other topics, check out our series outline:

Gson Series Overview

Gson Instance Creator

By default, Gson will always create an empty instance of the Java model and then set the properties via reflection. Strictly speaking, the constructors you create in your model are not used at all (by Gson). If your Java models act as simple data holders and provide no further logic or functionality, this behavior has no downsides.

However, if your data model does have additional properties that are set by default or dynamically, you'd have to do some hacking to get around Gson's use of reflection. Luckily, Gson covers that use case as well and lets you implement a clean solution with InstanceCreators.

Let's assume the server sends a fairly simple JSON:

{
  "age": 26,
  "email": "norman@fs.io",
  "isDeveloper": true,
  "name": "Norman"
}    

Your app has an equivalent Java model for it, which Gson can map without any further customization. However, there is one additional property in the client model: Context. The context could be necessary to store that Java object in a database. It won't come with the JSON and it makes no sense to map it. It's just a helper in our Java application.

Thus, we only have a single constructor in our Java model which requires that Context object:

public class UserContext {  
    private String name;
    private String email;
    private boolean isDeveloper;
    private int age;

    // additional attribute, which is not part of the data model
    private Context context;

    public UserContext(Context context) {
        this.context = context;
    }
}

If you'd use the model above with Gson's default behavior, context would always be null after deserialization. You need a way to call the constructor and set context before Gson does its mapping. For exactly that purpose Gson provides InstanceCreators.

The way you declare the custom InstanceCreators is very similar to the custom serializer, so it might be useful to catch up on that blog post.

String userSimpleJson = ...; // the JSON from the server, see above

GsonBuilder gsonBuilder = new GsonBuilder();  
gsonBuilder.registerTypeAdapter(  
    UserContext.class, 
    new UserContextInstanceCreator(getApplicationContext())
);
Gson customGson = gsonBuilder.create();

UserContext customObject = customGson.fromJson(userSimpleJson, UserContext.class);  

We're using a custom Gson instance and calling registerTypeAdapter() to add a new instance creator. The method expects two parameters. First, the type of the data model we want to adjust. Second, an implementation of the instance creator.

In our case, it's the UserContextInstanceCreator class:

private class UserContextInstanceCreator implements InstanceCreator<UserContext> {  
    private Context context;

    public UserContextInstanceCreator(Context context) {
        this.context = context;
    }

    @Override
    public UserContext createInstance(Type type) {
        // create new object with our additional property
        UserContext userContext = new UserContext(context);

        // return it to gson for further usage
        return userContext;
    }
}

The class has to implement the typed InstanceCreator interface and override the createInstance(Type type) method. The constructor of the UserContextInstanceCreator can be defined by us. In this case, we want to pass the Context to every model, so we're passing it to the InstanceCreator and storing it in a field. The instance creator will then call the UserContext(context) constructor whenever a new instance of the UserContext model will be created by Gson.

As a result, all following instances of the UserContext class have the Context set to the value you've passed to the instance creator. During the regular Java-JSON mapping will Gson still use reflection to set the properties.

Of course, you could do a variety of other things here. You might need to pass a bunch of other additional parameters, besides Context. Or your constructor does some initialization work, which need to be executed. By declaring an InstanceCreator you can make sure Gson is actually calling the constructor. If you've other examples, let us know in the comments!

Outlook

In this blog post, you've learned what custom InstanceCreators are and what they can be used for. The setup is quite simple and you can get rid of some hacking to make it work with Gson's default behavior. Make sure you know how you can utilize InstanceCreators, because you'll need them eventually.

If you've feedback or a question, let us know in the comments or on twitter @futurestud_io.

Make it rock & enjoy coding!

Explore the Library

Find interesting tutorials and solutions for your problems.