GSON Annotations Example

1 year ago by in Articles, GSON Tagged: , , , , , , ,

Gson (Homepage) provides a set of annotations to simplify the serialisation and deserialisation processes. In this article we will see how we can use these annotations and how these can simplify the use of Gson to convert between Java objects and JSON objects.

All code listed below is available at: https://java-creed-examples.googlecode.com/svn/gson/Gson Annotations Example. Most of the examples will not contain the whole code and may omit fragments which are not relevant to the example being discussed. The readers can download or view all code from the above link.

Gson provides four annotations, as documented in the Java Doc. These annotations can be grouped into three categories. Each category is discussed separately.

Custom Field Names

Consider the following simple class.

package com.javacreed.examples.gson.part1;

import com.google.gson.annotations.SerializedName;

public class Box {

  @SerializedName("w")
  private int width;

  @SerializedName("h")
  private int height;

  @SerializedName("d")
  private int depth;

  // Methods removed for brevity
}

This class has three fields that represent the width, height and depth of a box. These fields are annotated with the @SerializedName annotation (Java Doc). The parameter (value) of this annotation is the name to be used when serialising and deserialising objects. For example, the Java field depth is represented as d in JSON.

Gson support this annotation without the need of any configuration whatsoever as shown next.

package com.javacreed.examples.gson.part1;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Main {
  public static void main(final String[] args) {
    final GsonBuilder builder = new GsonBuilder();
    final Gson gson = builder.create();

    final Box box = new Box();
    box.setWidth(10);
    box.setHeight(20);
    box.setDepth(30);

    final String json = gson.toJson(box);
    System.out.printf("Serialised: %s%n", json);

    final Box otherBox = gson.fromJson(json, Box.class);
    System.out.printf("Same box: %s%n", box.equals(otherBox));
  }
}

The above code will print the following to the command prompt.

Serialised: {"w":10,"h":20,"d":30}
Same box: true

The first line is the output of the serialiser. Note how Gson uses the new names as the JSON fields names when serialising the Box Java object. The same applies for deserialising. Gson will remap the JSON field to the proper Java field name. This is verified by the equals() method which is overridden by the Box. The second line printed true, which means that the deserialised object is the same as the object originally created.

Observation

In order to simplify the examples shown here, we are simply confirming the result by printing it on the command prompt. While this is good for learning purposes, this is discouraged for development and instead JUnit (Homepage) (or other automated test suits) should be used instead.

This concludes our first example of how annotations can be used to simplify our work. The next example will illustrate how we can control exposure (what is serialised or not) using only annotations.

Control Exposure

Gson provides two types of conversion: serialisation (from Java to JSON) and deserialisation (from JSON to Java). Java fields marked transient are excluded from both serialisation and deserialisation. Therefore, sensitive information that should not be serialised can be marked as transient and Gson will not serialised to JSON.

Gson also provides finer serialisation and deserialisation control and filtration. With Gson we can control what is serialised and deserialised independently using only annotations. Alternatively, we can use a custom JsonDeserializer<T> (as described in the article Gson Deserialiser Example) and a custom JsonSerializer<T> (as described in the article Gson Serialiser Example). While these interfaces provide complete control and flexibility, but the annotation approach described in this article is simpler as it does not require additional classes as we will see in the follow example.

Consider the following class.

package com.javacreed.examples.gson.part2;

import com.google.gson.annotations.Expose;

public class Account {

  @Expose(deserialize = false)
  private String accountNumber;

  @Expose
  private String iban;

  @Expose(serialize = false)
  private String owner;

  @Expose(serialize = false, deserialize = false)
  private String address;

  private String pin;
}

Here we are using the @Expose annotation (Java Doc) which has two optional elements: deserialize and serialize. Through these two elements we can control what is serialised and deserialised independently as illustrated in the following table. The default values for the elements are both true.

Field Serialise Deserialise
@Expose(deserialize = false)
private String accountNumber;
YES NO
@Expose
private String iban;
YES YES
@Expose(serialize = false)
private String owner;
NO YES
@Expose(serialize = false, deserialize = false)
private String address;
NO NO
private String pin;
NO NO

In order to be able to work with this annotation, we need to use the proper configuration. Different from the @SerializedName annotation, we need to configure Gson to only expose fields that are annotated and ignore the rest as shown in the following code fragment.

    final GsonBuilder builder = new GsonBuilder();
    builder.excludeFieldsWithoutExposeAnnotation();
    final Gson gson = builder.create();

Without this configuration, this annotation is ignored and all eligible fields are serialised and deserialised including the field pin. Following is the complete example.

package com.javacreed.examples.gson.part2;

import java.io.InputStreamReader;
import java.io.Reader;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Main {
  public static void main(final String[] args) throws Exception {
    final GsonBuilder builder = new GsonBuilder();
    builder.excludeFieldsWithoutExposeAnnotation();
    final Gson gson = builder.create();

    final Account account = new Account();
    account.setAccountNumber("A123 45678 90");
    account.setIban("IBAN 11 22 33 44");
    account.setOwner("Albert Attard");
    account.setPin("1234");
    account.setAddress("Somewhere, Far Far Away");

    final String json = gson.toJson(account);
    System.out.printf("Serialised%n  %s%n", json);

    try (final Reader data = new InputStreamReader(Main.class.getResourceAsStream("account.json"), "UTF-8")) {

      final Account otherAccount = gson.fromJson(data, Account.class);
      System.out.println("Deserialised");
      System.out.printf("  Account Number: %s%n", otherAccount.getAccountNumber());
      System.out.printf("  IBAN:           %s%n", otherAccount.getIban());
      System.out.printf("  Owner:          %s%n", otherAccount.getOwner());
      System.out.printf("  Pin:            %s%n", otherAccount.getPin());
      System.out.printf("  Address:        %s%n", otherAccount.getAddress());
    }
  }
}

This example prints the following to the command prompt.

Serialised
  {"accountNumber":"A123 45678 90","iban":"IBAN 11 22 33 44"}
Deserialised
  Account Number: null
  IBAN:           IBAN 11 22 33 44
  Owner:          Albert Attard
  Pin:            null
  Address:        null

Note that only the account number and IBAN are serialised as illustrated in the table above. Furthermore, when deserialising the following JSON, we only processed the owner and the IBAN. The other fields were ignored.

{
  "accountNumber": "A123 45678 90",
  "iban":          "IBAN 11 22 33 44",
  "owner":         "Albert Attard",
  "address":       "Somewhere, Far Far Away",
  "pin":           "1234"
}

Using the @Expose annotation we can obtain fine grained control on what is serialised and deserialised without having to write code that handles this. This concludes our example about exposure control.

The following section discusses versioning and how this can be achieved using annotations.

Versioning

Gson provides two annotations called @Since (JavaDoc) and @Until (JavaDoc) which can be used to control which fields are serialised and deserialised when using Gson to convert between Java objects and JSON objects.

Consider the following class which describes a soccer player, playing in a local tournament. The player has four fields, where one of these fields was added in version 1.2and another field was (sort-of) removed in version 0.9.

package com.javacreed.examples.gson.part3;

import com.google.gson.annotations.Since;
import com.google.gson.annotations.Until;

public class SoccerPlayer {

  private String name;

  @Since(1.2)
  private int shirtNumber;

  @Until(0.9)
  private String country;

  private String teamName;

  // Methods removed for brevity
}

Similar to the @Expose annotation described before, in order for this annotation to be effective, we need to make the proper configurations as shown next.

package com.javacreed.examples.gson.part3;

import java.io.InputStreamReader;
import java.io.Reader;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class Main {
  public static void main(final String[] args) throws Exception {

    final GsonBuilder builder = new GsonBuilder();
    builder.setVersion(1.0);

    final Gson gson = builder.create();

    final SoccerPlayer account = new SoccerPlayer();
    account.setName("Albert Attard");
    account.setShirtNumber(10); // Since version 1.2
    account.setTeamName("Zejtun Corinthians");
    account.setCountry("Malta"); // Until version 0.9

    final String json = gson.toJson(account);
    System.out.printf("Serialised (version 1.0)%n  %s%n", json);

    try (final Reader data = new InputStreamReader(Main.class.getResourceAsStream("player.json"), "UTF-8")) {
      // Parse JSON to Java
      final SoccerPlayer otherPlayer = gson.fromJson(data, SoccerPlayer.class);
      System.out.println("Deserialised (version 1.0)");
      System.out.printf("  Name:          %s%n", otherPlayer.getName());
      System.out.printf("  Shirt Number:  %s (since version 1.2)%n", otherPlayer.getShirtNumber());
      System.out.printf("  Team:          %s%n", otherPlayer.getTeamName());
      System.out.printf("  Country:       %s (until version 0.9)%n", otherPlayer.getCountry());
    }
  }
}

In this example we defined the version to be 1.0, which means that only the player name and the team name will be processed. The shirt number was added in version 1.2, therefore not eligible and the country was removed after version 0.9. The above will print the following to the command prompt.

Serialised (version 1.0)
  {"name":"Albert Attard","teamName":"Zejtun Corinthians"}
Deserialised (version 1.0)
  Name:          Albert Attard
  Shirt Number:  0 (since version 1.2)
  Team:          Zejtun Corinthians
  Country:       null (until version 0.9)

This example concludes our article about the Gson annotations. As shown here, these annotations provide a great deal of control without having to write complex classes. With that said, the annotations have their limitations and cannot handle all cases such as translating between different structures or caching similar objects. When such particular case arise, we should use the JsonDeserializer<T> (discussed here) and a custom JsonSerializer<T> (discussed here) respectively.

Albert Attard

Albert Attard is a Java passionate and technical lead at a research group. You can find him on . Over the past years Albert worked on various Java projects including traditional server/client applications, modular applications, large data handling applications and concurrent data manipulation applications to name a few. He has a BSc degree from the University of London (Homepage) and an MSc Information Security with the same university. His MSc thesis (Book) received the 2012 SearchSecurity.co.UK award (Website).

3 Responses to “GSON Annotations Example”


jagadeesh
June 18, 2013 Reply

This is good article, i was waiting for this type of elaboration.
Thanks for you explanation.

Niranjan
January 17, 2014 Reply

Can you give some example how to process my own custom annotations? For an example I want to introduce annotations like @Summary, @Detail for attributes (because I want to generate different views from the same POJO). GSON controls annotations through its Excluder class, but I am not sure how can I hook my custom annotations to GSON.

Appreciate if you could give some pointers.

Thanks,
NN

Albert Attard Albert Attard
January 17, 2014 Reply

Why don’t you use a custom deserialiser as described: here?

Leave a Comment



seven + 5 =