Java Google Json (Gson) New in Gson 2.3

What’s new in Gson 2.3

In this article we look at the additions in Google Json 2.3. There are three main changes :

we now present examples for all three

@TypeAdapter Annotation

TypeAdapters are used to specify rules to convert a java class to json and other way round. Look at this tutorial to understand how type adapters work. We look at the same example but use an annotation instead. The example deals with parsing the free music api gson string. We hit the free music api and get the top 5 albums along with the datasets of each album. Look at this link to see the json. The Album info is stored in the Album class and the datasets in the Dataset class. Here are the classes

// The album class stores the album element of the json. It contains the title and a list of Datasets.
public class Album {

	private String title;
	private Dataset[] dataset;

	public void setTitle(String title) {
		this.title = title;
	}

	public void setDataset(Dataset[] dataset) {
		this.dataset = dataset;
	}

	public String getTitle() {
		return title;
	}

	public Dataset[] getDataset() {
		return dataset;
	}
}
//-------------------------------------------------------------------
// We use a type adapter for the Dataset since we want to serialize the dataset differently then what is provided by default.
import com.google.gson.annotations.JsonAdapter;

@JsonAdapter(DatasetTypeAdapter.class)
public class Dataset {
	private String album_id;
	private String album_title;

	public String getAlbum_id() {
		return album_id;
	}

	public void setAlbum_id(String album_id) {
		this.album_id = album_id;
	}

	public String getAlbum_title() {
		return album_title;
	}

	public void setAlbum_title(String album_title) {
		this.album_title = album_title;
	}

}

Notice how the Dataset class is annotated with the @JsonAdapter. Lets look at the adapter

/**
 * 
 * album_title and album_url are two distinct fields in the dataset element of the json.
  The Dataset object contains the field album_title. Normally Gson would map the
 * album_title property in the json with the album_title field in the Dataset
 * object. However, we dont want that. We want to use the album_url property
 * from the json object to populate the album_title field in the Dataset2 object.
 * we build a custom TypeAdapter to do that. This is just a trivial case, you
 * could also combine album_url and album_title properties and set it to the
 * album_title field of the Dataset Object.
 * 
 */
public class DatasetTypeAdapter extends TypeAdapter<Dataset> {
	@Override
	public Dataset read(JsonReader reader) throws IOException {
		// the first token is the start object
		JsonToken token = reader.peek();
		Dataset dataset = new Dataset();
		if (token.equals(JsonToken.BEGIN_OBJECT)) {
			reader.beginObject();
			while (!reader.peek().equals(JsonToken.END_OBJECT)) {
				if (reader.peek().equals(JsonToken.NAME)) {
					if (reader.nextName().equals("album_url"))
						dataset.setAlbum_title(reader.nextString());
					else
						reader.skipValue();

				}
			}
			reader.endObject();

		}
		return dataset;
	}

	@Override
	public void write(JsonWriter out, Dataset value) throws IOException {

	}

}

As explained in the comments we use a TypeAdapter since we dont want to populate the Dataset object in the default way, but use special rules. Here’s the main method

public class DatasetTypeAdapterUsingAnnotation {

	public static void main(String[] args) throws MalformedURLException, IOException {
		String url = "http://freemusicarchive.org/api/get/albums.json?api_key=60BLHNQCAOUFPIBZ&limit=5";
		String json = IOUtils.toString(new URL(url));
		Gson gson = new GsonBuilder().create();
		// deserialize the json to Albums class. The Dataset objects are part
		// of the Albums class. Whenever Gson encounters an object of type DataSet
		// it calls the DatasetTypeAdapter to read and write json.
		Album albums = gson.fromJson(json, Album.class);
		System.out.println(albums.getDataset()[1].getAlbum_title());
		// prints http://freemusicarchive.org/music/The_Yes_Sirs/Through_The_Cracks_Mix_Vol_1/
	}

}

Next, lets look at the JsonPath support added in gson 2.3

JsonPath Support

To understand JsonPath look at this link. JsonPath is to Json what XPath is to XML. Gson has a new method in JsonReader class that gives the path of the element where the JsonReader cursor is. To demonstrate JsonPath we use the same example as above, i.e. the music archive. We print the json path for each string element.

public class JsonPathExample {
	public static void main(String[] args) throws MalformedURLException, IOException {
		String url = "http://freemusicarchive.org/api/get/albums.json?api_key=60BLHNQCAOUFPIBZ&limit=1";
		String json = IOUtils.toString(new URL(url));
		JsonReader reader = new JsonReader(new StringReader(json));
		System.out.println(reader.getPath());
		handleObject(reader);
	}

	private static void handleObject(JsonReader reader) throws IOException {
		reader.beginObject();
		while (reader.hasNext()) {
			JsonToken token = reader.peek();
			if (token.equals(JsonToken.BEGIN_ARRAY))
				handleArray(reader);
			else if (token.equals(JsonToken.END_ARRAY)) {
				reader.endObject();
				return;
			} else
				handleNonArrayToken(reader, token);
		}

	}

	public static void handleArray(JsonReader reader) throws IOException {
		reader.beginArray();
		while (true) {
			JsonToken token = reader.peek();
			if (token.equals(JsonToken.END_ARRAY)) {
				reader.endArray();
				break;
			} else if (token.equals(JsonToken.BEGIN_OBJECT)) {
				handleObject(reader);
			} else
				handleNonArrayToken(reader, token);
		}
	}

	public static void handleNonArrayToken(JsonReader reader, JsonToken token) throws IOException {
		 if (token.equals(JsonToken.STRING)) {
			System.out.println(reader.getPath());
			reader.skipValue();
		}  else
			reader.skipValue();
	}
}

The output of the program is show below. It gives an idea of how the jsonpath looks like.

$
$.title
$.message
$.total
$.limit
$.dataset[0].album_id
$.dataset[0].album_title
$.dataset[0].album_handle
$.dataset[0].album_url
$.dataset[0].album_type
$.dataset[0].artist_name
$.dataset[0].artist_url
$.dataset[0].album_information
$.dataset[0].album_comments
$.dataset[0].album_favorites
$.dataset[0].album_tracks
$.dataset[0].album_listens
$.dataset[0].album_date_created
$.dataset[0].album_image_file
$.dataset[0].album_images[0].image_id
$.dataset[0].album_images[0].album_id
$.dataset[0].album_images[0].image_file
$.dataset[0].album_images[0].image_title
$.dataset[0].album_images[0].image_order

The last part is the new methods in jsonarray

New Methods in JsonArray

We look at four new methods added in JsonArray : contains(JsonElement), remove(JsonElement), remove(int index), set(int index, JsonElement element). The contains method checks whether the array contains the particular element, the remove method has two variants: one that takes in an index and other that takes in the actual element to be removed. The last method is the set method that sets a new element at a particular index. The example below demonstrates all four methods.

public class JsonArrayExample {
	public static void main(String[] args) {

		JsonArray array = new JsonArray();
		array.add(new JsonPrimitive("StringA"));
		JsonObject a = new JsonObject();
		a.add("key1", new JsonPrimitive("value1"));
		JsonObject b = new JsonObject();
		b.add("key2", new JsonPrimitive("value2"));
		array.add(a);
		System.out.println(array.toString());
		// prints ["StringA",{"key1":"value1"}]

		// the new methods now
		System.out.println(array.contains(a));// prints true
		System.out.println(array.contains(b));// prints false
		array.set(1, b);
		System.out.println(array.toString());
		// prints ["StringA",{"key2":"value2"}]. the value at index 1 is
		// overwritten
		System.out.println(array.remove(a)); // prints false since a is not
												// present in the array
		System.out.println(array.remove(1));
		// prints {"key2":"value2"}, the element that is removed
		System.out.println(array.toString());
		//prints ["StringA"]

	}
}

Leave a Comment