JSON with GSON and abstract classes

I have switched to Google Gson after many years of using org.json library for supporting JSON data interchange format in Java. org.json is a lower-level library, so that you have to create JSONObject, JSONArray, JSONString, … and do other low-level work. Gson simplifies this work. It provides simple toJson() and fromJson() methods to convert arbitrary Java objects to JSON and vice-versa, supports Java Generics, allows custom representations for objects, generates compact and readability JSON output and has many other goodies. I love it more and more. The using is simple. Assume, we have a class called Circle.

public class Circle {
    private int radius = 10;
    private String backgroundColor = "#FF0000";
    private String borderColor = "#000000";
    private double scaleFactor = 0.5;
    ...

    // getter / setter
}

Serialization (Java object –> JSON) can be done as follows:

Circle circle = new Circle();
Gson gson = new Gson();
String json = gson.toJson(circle); 
==> json is
{
    "radius": 10,
    "backgroundColor": "#FF0000",
    "borderColor": "#000000",
    "scaleFactor": 0.5,
    ...
}

Deserialization (JSON –> Java object) is just one line of code:

Circle circle2 = gson.fromJson(json, Circle.class);  
==> circle2 is the same as the circle above

Everything works like a charm. There is only one problem I have faced with abstract classes. Assume, we have an abstract class AbstractElement and many other classes extending this one

public abstract class AbstractElement {
    private String uuid;

    // getter / setter
}

public class Circle extends AbstractElement {
   ...
}

public class Rectangle extends AbstractElement {
   ...
}

public class Ellipse extends AbstractElement {
   ...
}

Assume now, we store all concrete classes in a list or a map parametrized with AbstractElement

public class Whiteboard
{
    private Map<String, AbstractElement> elements = 
            new LinkedHashMap<String, AbstractElement>();
    ...
}

The problem is that the concrete class is undisclosed during deserialization. It’s unknown in the JSON representation of Whiteboard. How the right Java class should be instantiated from the JSON representation and put into the Map<String, AbstractElement> elements? I have nothing found in the documentation what would address this problem. It is obvious that we need to store a meta information in JSON representations about concrete classes. That’s for sure. Gson allows you to register your own custom serializers and deserializers. That’s a power feature of Gson. Sometimes default representation is not what you want. This is often the case e.g. when dealing with third-party library classes. There are enough examples of how to write custom serializers / deserializers. I’m going to create an adapter class implementing both interfaces JsonSerializer, JsonDeserializer and to register it for my abstract class AbstractElement.

GsonBuilder gsonBilder = new GsonBuilder();
gsonBilder.registerTypeAdapter(AbstractElement.class, new AbstractElementAdapter());
Gson gson = gsonBilder.create();

And here is AbstractElementAdapter:

package com.googlecode.whiteboard.json;

import com.google.gson.*;
import com.googlecode.whiteboard.model.base.AbstractElement;
import java.lang.reflect.Type;

public class AbstractElementAdapter implements JsonSerializer<AbstractElement>, JsonDeserializer<AbstractElement> {
    @Override
    public JsonElement serialize(AbstractElement src, Type typeOfSrc, JsonSerializationContext context) {
        JsonObject result = new JsonObject();
        result.add("type", new JsonPrimitive(src.getClass().getSimpleName()));
        result.add("properties", context.serialize(src, src.getClass()));

        return result;
    }

    @Override
    public AbstractElement deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();
        String type = jsonObject.get("type").getAsString();
        JsonElement element = jsonObject.get("properties");

        try {
            return context.deserialize(element, Class.forName("com.googlecode.whiteboard.model." + type));
        } catch (ClassNotFoundException cnfe) {
            throw new JsonParseException("Unknown element type: " + type, cnfe);
        }
    }
}

I add two JSON properties – one is ” type” and the other is ” properties”. The first property holds a concrete implementation class (simple name) of the AbstractElement and the second one holds the serialized object itself. The JSON looks like

{
    "type": "Circle",
    "properties": {
        "radius": 10,
        "backgroundColor": "#FF0000",
        "borderColor": "#000000",
        "scaleFactor": 0.5,
        ...
    }
}

We benefit from the ” type” property during deserialization. The concrete class can be instantiated now by Class.forName(“com.googlecode.whiteboard.model.” + type) where “com.googlecode.whiteboard.model.” + type is a fully qualified class name. The following call

public <T> T deserialize(JsonElement json, Type typeOfT) throws JsonParseException

from JsonDeserializationContext invokes default deserialization on the specified object and completes the job.

Reference: JSON with GSON and abstract classes from our JCG partner Oleg Varaksin at the Thoughts on software development blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

4 Responses to "JSON with GSON and abstract classes"

  1. LG Optimusv says:

    How is that gson compared to ObjectMapper of Jackson?

    • TatuSaloranta says:

      With GSON, author wrote his own system to do this (no default support I think). 
      With Jackson you could just use @JsonTypeInfo annotation or enable default typing; both come out-of-the-box.

  2. Svend Vanderveken says:

    Hi,

    Thanks for the postThis approach is very similar to the way Jackson handles polymorphism. The advantage of Jackson though is that it offers built-in mechanism for that, so there is no need to code specific serializer/deserializer manually.

    Have a look here:

    http://wiki.fasterxml.com/JacksonPolymorphicDeserialization 

    Svend

  3. KevinO says:

    I have met this abstract class problem half a year ago, because of the type erasure, TypeToken does not work. so lots of “IF..ELSE” code in my project.
    Your solution is so good.

Leave a Reply


− four = 4



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.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close