Gson: Serializing and deserializing Java Generic Classes

Serializing list

As we saw in the previous tutorial serializing and deserializing classes with generic types is non trivial since generic type information is lost while serializing. Gson provides a class called com.google.gson.reflect.TypeToken to store generic types. The example below shows how to use the TypeToken class to serialize and deserialize Classes with generic types.

package com.studytrails.json.gson;

import java.lang.reflect.Type;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class GenericTypesExample8 {
	public static void main(String[] args) {
		// create an animal class that is of type dog.
		Animal animal = new Animal<Dog>();
		// Create a Dog instance
		Dog dog = new Dog("I am a dog");

		animal.setAnimal(dog);
		Gson gson = new Gson();
		// Define a Type that is an Animal of type dog.
		Type animalType = new TypeToken<Animal<Dog>>() {
		}.getType();

		// we first convert the animal object to a json and then read the json
		// back. However we define the json to be of Animal type
		Animal animal1 = gson.fromJson(gson.toJson(animal, animalType), Animal.class);
		System.out.println(animal1.get().getClass()); // prints class

    // com.google.gson.internal.LinkedTreeMap
		// In contrast to above where we read the json back using the Animal
		// type, here we read the json back as the custom animalType Type. This
		// gives Gson an idea of what
		// the generic type should be.
		Animal animal2 = gson.fromJson(gson.toJson(animal), animalType);
		System.out.println(animal2.get().getClass());
		// prints class com.studytrails.json.gson.Dog

	}
}

Gson: Serializing Collections

Serializing list

Serializing Collections should have been similar to serializing other objects. However, the problem is that Collections are generic and the generic type information is not maintained in the json. We therefore pass the type while deserializing list. Note that if the Collection has different types of objects then there is no way to serialize it.

package com.studytrails.json.gson;

import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class DeSerializeListExample5 {
	public static void main(String[] args) {
		String json = "[{album_id:1,album_title:'album1'},{album_id:2,album_title:'album2'}]";

		Gson gson = new Gson();
		// create the type for the collection. In this case define that the collection is of type Dataset
		Type datasetListType = new TypeToken<Collection<Dataset>>() {}.getType();
		List<Dataset> datasets = gson.fromJson(json, datasetListType);
		for (Dataset dataset : datasets) {
			System.out.println(dataset.getAlbum_title());
			System.out.println(dataset.getAlbum_id());
		}
		// Prints
		//album1
		//1
		//album2
		//2

	}
}

Gson: Convert json to a java object tree

In the Earlier tutorial we saw how to convert json to a java object. In this tutorial, we build a tree of com.google.gson.JsonElement from the json string. The tree can then be traversed to build java objects. JsonElement has methods such as isJsonObject(), isJsonNull(), etc that can be used to figure out the type of JsonElement. Then to get the actual object use the getAsJsonObject(), getAsJsonPrimitive() etc methods. We parse the response from the free music archive json API. Here’s the class

Gson: parsing json to java token by token

In the earlier tutorials we saw how to convert json to a java object. In This tutorial we see how to parse json and obtain individual tokens. Although this may seem like a cumbersome way to build java object from json, however it is extremely powerful and may be a good choice if you need a very high level of control over the parsing. We use the JsonReader class to read the json as a stream of tokens. The beginning of an object or an array is also a token. Here’s a detailed example.

Gson: Convert json to a java object

Google json provides methods to convert the json string to java objects. The Java object may be hierarchical. For this example we consider java objects of non generic type only. Gson uses the name to match the json property to the java field. There are two ways to convert json to java.

  • Using the com.google.gson.Gson class. Create a new instance of this class and use the method public T fromJson(String json, Class classOfT). classOfT is the java object to which the json is to be converted to.
  • The other way is to use the com.google.gson.GsonBuilder class. This class allows setting up certain features, such as - allowing null serialization or setting up custom serializing policies. Create a GsonBuilder, apply the features and then obtain the Gson class from the builder.

In this tutorial we look at the Gson class to de-serialize json from free music archive. The main class is the Albums class and it contains the list of Datasets. Each Dataset is one album. The way to approach the problem of deserialization is to build a java class such that the when Gson converts the Java class to JSON, the resultant JSON resembles the one we are trying to parse. Lets see this in action. If you look at the JSON, it starts with a root object that has properties such as title, message, errors, etc.

{
title: "Free Music Archive - Albums",
message: "",
errors: [ ],
total: "11259",
total_pages: 2252,
page: 1,
limit: "5",
dataset: [
{
......

Gson: Introduction

google json - gson is an open source java api for parsing and building json. It has extensive support for java generics. It also provides support for converting third party classes to json. It can be used to serialize and deserialize complex objects with deep hierarchies that may contain generic classes. In these tutorials we demonstrate, with examples, the following functionalities of gson.

Gson 2.3: Look at this tutorial to see the latest additions in Gson 2.3 (TypeAdapter Annotation, JsonPath support and new methods in JsonArray)

  1. Java to JSON and Back - Data Binding - In this example we look at how to bind a Json to a java object. Gson is quite powerful when it comes to binding a json to Java since it has a lot of built in serializers and deserializers. A serializer has code that helps in converting a Json string to corresponding java type. For example if you have an array in JSON (elements enclosed in ‘[’ and ‘]’) and you want to convert that to a Java array then Gson would internally use an ArrayTypeAdapter to convert the Json Array to Java Array and back. If you are looking at Gson primarily to parse JSON then we would suggest that you start with a simple Java class, convert that to JSON and make sure it looks like the JSON that you want to parse. You can then gradually start adding complexities to the java class and at each step convert it to a JSON and ensure that the JSON is similar to the one that you are trying to parse. It is good to think of parsing a JSON as creating a java class that can give you the JSON that you want to parse. Also note that you have certain flexibility while creating the java object. You could represent a Json Array as a Java array or a Java List. The choice may be driven more by how you want to use the class. If you map a Json property to a java array Gson would use an Array adapter but if you map it to a List it would use the collection adapter and you, the user, does not have to worry about that. Gson also handles null gracefully

  2. Java to JSON and Back - Streaming - At certain times you want more control on the way the parsing is done. Also there may be times when Gson automated conversion does not give you the result that you are looking for. For example, if you have a List that contains multiple kinds of Object then Gson may not be able to deserialize or parse the Json for you. In such cases Gson provides a streaming API. You can use the Streaming API and handle a token at a time. The token may be start and end of Json Object, start and end of json array, the key of a property and the String or Number value of a property.

Jackson: Json Polymorphism

Jackson provides a way to maintain sub type information while serializing java objects. It is possible to recreate the exact sub type. The type information can be embedded into the json as a property. In the example below we create a zoo, that has a list of animals. The animal may be an elephant or a lion, and they both extend the Animal abstract class. While deserializing we want to create the exact animal type. We also demonstrate the use of @JsonTypeInfo and @JsonSubTypes annotations.

Data Serialization and Polymorphism Example

package com.studytrails.json.jackson;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SerializeExample1 {
    private static String outputFile = "zoo.json";

    public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
        // let start creating the zoo
        Zoo zoo = new Zoo("Samba Wild Park", "Paz");
        Lion lion = new Lion("Simba");
        Elephant elephant = new Elephant("Manny");
        List<Animal> animals = new ArrayList<>();
        animals.add(lion);
        animals.add(elephant);
        zoo.setAnimals(animals);

        ObjectMapper mapper = new ObjectMapper();
        mapper.writerWithDefaultPrettyPrinter().writeValue(new FileWriter(new File(outputFile)), zoo);
    }
}

Jackson: jackson Mix- In Annotations

Annotations are a great way to manage serialization and deserialization in Jackson. However, what do you do if you want to annotate a third party class, or if you dont want to tightly couple your POJOs to jackson annotations. This is where Mix-in comes into play. You define a mix-in abstract class that is kind of a proxy to the actual class. Annotations are then definied over this proxy class

Jackson Mix-In Example

package com.studytrails.json.jackson;

import java.io.File;
import java.io.IOException;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class SerializeExample3 {
    public static void main(String[] args) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.addMixInAnnotations(Bird.class, BirdMixIn.class);
        Bird bird = new Bird("scarlet Ibis");
        bird.setSound("eee");
        bird.setHabitat("water");

        mapper.writerWithDefaultPrettyPrinter().writeValue(new File("bird.json"), bird);
    }

}

Jackson: Json data binding filters

Jackson provides an effective an efficient way to bind json to POJOs. However, at times, certain properties may need to be ignored while converting a json to java ojbect and a java object to json string. Jackson provides three ways to filter properties.

  1. @JsonIgnoreProperties- This annotation can be used at the type level to ignore json properties. In the example below we ignore the ’tags’ property from the albums dataset.
  2. @JsonIgnore - This annotation can be set at property level to ignore certain properties.
  3. Using Custom filters

The example below shows method 1 and 2. Also note the use of the @JsonAutoDetect annotation.

Data Binding Filters Example

Databinding

package com.studytrails.json.jackson;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonProperty;

// Do not use fields to autodetect. use the public getter methods to autodetect properties
@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.PUBLIC_ONLY)
public class AlbumsFilter {

    private String title;
    private DatasetFilter[] datasetFilter;
    public String total_pages;

    protected String getTotal_pages() {
        return total_pages;
    }

    public String getTitle() {
        return title;
    }

    // this getter method is for the 'dataset' property
    @JsonProperty("dataset")
    public DatasetFilter[] getDatasetFilter() {
        return datasetFilter;
    }
}