What is Gson?
Gson is a Java Library widely used to convert Java Objects into their JSON representation and vice versa.
Getting Started
Let's start by creating a new Java project. We can call it "InterfaceSerialization".
We need to add the gson library in our project in order for us to use it. If you are using Maven, add the latest version of gson to the POM file.
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.3.1</version> </dependency>
Step 1:
Now, let's create our Java interface. This time I will be using "Cars" as an example. I love cars, who doesn't?
Our interface declares three features that all cars share.
public interface Car { String model(); int maxSpeed(); String type(); }
Step 2:
Let's now create our Java classes that will implement our "Car" interface. I will be using "Lexus" and "Acura" (my favorite brands) for this example.
Let’s define our Lexus class:
public class Lexus implements Car { private int maxSpeed; private String type; private int rankingAmongSedans; private String model; public Lexus(String model, int maxSpeed, String type, int rankingAmongSedans) { this.model = model; this.maxSpeed = maxSpeed; this.type = type; this.rankingAmongSedans = rankingAmongSedans; } public String model() { return model; } public int maxSpeed() { return maxSpeed; } public String type() { return type; } public int getRankingAmongSedans() { return rankingAmongSedans; } @Override public String toString(){ return "Model: " + model + ", Max speed: " + maxSpeed + " km/h, Type: " + type + ", Ranking: " + rankingAmongSedans ; } }
Step 3:
In the same way we have to define our Acura class
public class Acura implements Car{ private int maxSpeed; private String type; private String model; public Acura(String model, int maxSpeed, String type) { this.model = model; this.maxSpeed = maxSpeed; this.type = type; } public String model() { return null; } public int maxSpeed() { return maxSpeed; } public String type() { return type; } @Override public String toString() { return "Model: " + model + ", Max speed: " + maxSpeed + " km/h, Type: " + type; } }
Notice that the Lexus class contains an extra field called "rankingAmongSedans". This will help us to distinguish between the two and prove that our serialization works properly.
Step 4:
Now let’s define our test class:
public class Test { public static void main(String[] args) { //Let's initialize our sample array of cars Car cars[] = new Car[]{new Lexus("Lexus Is", 260, "Sedan", 3), new Acura("Acura Mdx", 193, "Suv")}; //Create our gson instance Gson gson = new Gson(); //Let's serialize our array String carsJsonFormat = gson.toJson(cars, Car[].class); //Let's Print our serialized arrya System.out.println("Cars in Json Format: " + carsJsonFormat); } }
In this class, we are basically creating an array of "Cars" with two car objects, one being of Class Type Lexus, and the other one of Class Type Acura
Then we instantiate a gson object called “Gson” and proceed to serialize our array. Finally we print our array in JSON format.
Our output should look like this:
Cars in JSON format:
[{"maxSpeed":260,"type":"Sedan","rankingAmongSedans":3,"model":"Lexus Is"},{"maxSpeed":193,"type":"Suv","model":"Acura Mdx"}]
It seems that everything went fine, our array of cars is printed in JSON format; however, what happens when we try to deserialize the String back to an array of Car Objects?
Let's add this line code after our print statement and try to re run our test again.
Car [] carJsonArray = gson.fromJson(carsJsonFormat, Car[].class);
Oops, did you get this? RunTimeException: "Unable to invoke no-args constructor for interface Car. Register an InstanceCreator with Gson for this type may fix this problem."
Gson doesn't have way to identify which "Car Object" we are trying to deserialize. We need to register a Type Adapter that will help gson differentiate the objects.
Step 5: (The solution)
So let's define a Generic Interface Adapter (so we are not only restricted to our car interface) that will override serialize and deserialize methods.
The magic happens in this class, as we define a mapping structure to distinguish our Car Objects, where "CLASSNAME" is the key to get the object's class name, and "DATA" is the key that maps the actual JSON object.
public class InterfaceAdapterimplements JsonSerializer , JsonDeserializer { private static final String CLASSNAME = "CLASSNAME"; private static final String DATA = "DATA"; public T deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { JsonObject jsonObject = jsonElement.getAsJsonObject(); JsonPrimitive prim = (JsonPrimitive) jsonObject.get(CLASSNAME); String className = prim.getAsString(); Class klass = getObjectClass(className); return jsonDeserializationContext.deserialize(jsonObject.get(DATA), klass); } public JsonElement serialize(T jsonElement, Type type, JsonSerializationContext jsonSerializationContext) { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty(CLASSNAME, jsonElement.getClass().getName()); jsonObject.add(DATA, jsonSerializationContext.serialize(jsonElement)); return jsonObject; } /****** Helper method to get the className of the object to be deserialized *****/ public Class getObjectClass(String className) { try { return Class.forName(className); } catch (ClassNotFoundException e) { //e.printStackTrace(); throw new JsonParseException(e.getMessage()); } } } }
Step 6:
Now, let's go back to our test class and add the registerTypeAdapter to our gson object. Since we are using a customized gson object, we need to construct it with "gsonBuilder".
Let's replace Gson gson = new Gson(); with:
//Create our gson instance GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Car.class, new InterfaceAdapter()); Gson gson = builder.create();
Now that we have registered our InterfaceAdapter, we’ll add these lines of code to print our results:
//Let's print our car objects to verify System.out.println("\n**********************************************************************"); System.out.println("My favorite Cars of 2015"); for(Car aCar : carJsonArray){ System.out.println(aCar); } System.out.println("**********************************************************************");
Let's test again and see what happens. After running our test class, we should see the following output: Cars in JSON Format:
[{"CLASSNAME":"Lexus","DATA":{"maxSpeed":260,"type":"Sedan","rankingAmongSedans":3,"model":"Lexus Is"}},{"CLASSNAME":"Acura","DATA":{"maxSpeed":193,"type":"Suv","model":"Acura Mdx"}} ]
**********************************************************************
My favorite Cars of 2015
Model: Lexus Is, Max speed: 260 km/h, Type: Sedan, Ranking: 3
Model: Acura Mdx, Max speed: 193 km/h, Type: Suv
**********************************************************************
We were able to achieve our goal using gson by just adding an interface adapter. It’s up to you how to customize the serialization of different data types.