Tuesday 30 June 2009

Practical Example of GSON

Please note that this page has moved to: http://www.javacreed.com/simple-gson-example/.

GSON is a Java API, developed by Google, used to convert between Java objects and JSON objects. This article discusses and provides examples about this API and how it can be used. More information about this API can be found at: http://sites.google.com/site/gson/.

This is the first from three articles about GSON. No GSON or JSON experience is required as this article acts as a primer for the other two articles. The second article provides more examples about the use of GSON deserializer (from JSON to Java) and the third and final article provides more examples about the GSON serializer (from Java to JSON). All code for all three articles is available at: http://code.google.com/p/gson-practical-examples/source/checkout.

Download and Install

Before you can do any work with this API, you need to download the library (jar file) and include it in the classpath. The library, together with the source and JavaDocs, can be downloaded from: http://code.google.com/p/google-gson/downloads/list. Once downloaded, add the gson-<version>.jar to the classpath. For those readers who prefer to use maven to manage the dependencies (JAR files), add the following dependency to the pom.xml.

    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.2.1</version>
    </dependency>

Change the <version>2.2.1</version> as required. All code examples shown in this article use the version listed above. A copy of the pom.xml file can be found here.

If this library is to be used in a web application, make sure to have a copy saved under the WEB-INF/lib folder. Alternatively, the library can be added to the application server and made available to the web application.

A Simple Example

The GSON API provides a stateless class, Gson, that handles the conversions between Java and JSON objects. An instance of this class can be created by invoking the default constructor or as shown in the simple example below, using the GsonBuilder class. The GsonBuilder class provides customisation and allows the developer to instantiate Gson as required.

package com.albertattard.examples.gson.part1_1;

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

public class SimpleExample1 {
  public static void main(String[] args) {
    Gson gson = new GsonBuilder().create();
    gson.toJson("Hello", System.out);
    gson.toJson(123, System.out);
  }
}
In the above example, we've created an instance of Gson and converted the Java String and int to JSON objects. The above code produces the following as its output to the command prompt:
"Hello"123
It's not rocket science, but it's a start. Note that the output of the above is all going to the command prompt. The toJason() method takes two parameters, the Java object to be converted to JSON and an instance of Appendable. We can easily change the out to a file or network stream.

package com.albertattard.examples.gson.part1_1;

import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class SimpleExample2 {
  public static void main(String[] args) throws IOException {
    Writer writer = new FileWriter("Output.json");

    Gson gson = new GsonBuilder().create();
    gson.toJson("Hello", writer);
    gson.toJson(123, writer);

    writer.close();
  }
}
Why is the variable declared as a Writer when the actual type is FileWriter?
It is a good practice to have your variables as generic as possible. In the above example, we're only using methods defined by the Appendable and Writer interfaces. Having the variable type more specific than required will make the code less portable and harder to maintain as we'll see in the following example.

Note that in the above example we are not handling the steams (Writer) properly. Ideally the resources are closed within the finally block or used within the try-with-resources as shown below. We've ignored this to keep the code as simple as possible.


  public static void main(String[] args) throws IOException {
    try (Writer writer = new FileWriter("Output.json")) {

      Gson gson = new GsonBuilder().create();
      gson.toJson("Hello", writer);
      gson.toJson(123, writer);
    }
  }

The above code produces the file: Output.json with the JSON objects. Note that here we used the character streams and not the byte streams. We cannot use the byte streams as the toJson() method is expecting an Appendable instance and the byte streams are not descendants from the Appendable interface. The Appendable interface works with characters and not bytes. Java provides the InputStreamReader and the OutputStreamWriter classes that convert byte streams into character streams as illustrated in the following example.


package com.albertattard.examples.gson.part1_1;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class SimpleExample3 {

  public static void main(String[] args) throws IOException {
    Writer writer = new OutputStreamWriter(
            new FileOutputStream("Output.json"));

    Gson gson = new GsonBuilder().create();
    gson.toJson("Hello", writer);
    gson.toJson(123, writer);

    writer.close();
  }
}
As you can see, we only needed to change the instantiation part. The rest (expect for the imports) of the code is unchanged.

Consuming JSON objects

Let's say that we need to consume JSON objects and load them as Java objects. Assume that a web server will produce the following JSON when queried:

{
  NAME:"Albert Attard",
  P_LANGUAGE:"Java",
  LOCATION:"Malta"
}
This JSON object contains three fields with their respective values. Let's say that we need to consume the JSON object and create a Java object that represents this data. To make this example more interesting, let assume that we're only interested from the name and the location fields.

First we need to create a Java class with the fields that we want to represent (name and location). Let's call the class Person1. The name of this class is irrelevant, but the name of the fields is not. The field names must match (including the case) with the names in JSON. Also, the class must include a default constructor. As shown below, the fields name and location are in uppercase as found in JSON. The JSON field P_LANGUAGE is ignored as the Java object does not include a field with this name. It is understandable that the fields' names do not follow the Java naming convention, but for the time being let's keep things simple. More about this is discussed in part 2.


package com.albertattard.examples.gson.part1_2;

public class Person1 {
  private String NAME;
  private String LOCATION;

  // Getters and setters are not required for this example.
  // GSON sets the fields directly.

  @Override
  public String toString() {
    return NAME + " - " + LOCATION;
  }
}
With the Java object ready, we can read the JSON objects and load them as Java objects as illustrated below. To simulate a real life situation, we're using a byte stream as input with default encoding. Also note that the JSON content is saved into a file (which is not usually the case) located in the same directory of the following class.

package com.albertattard.examples.gson.part1_2;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class JsonToJava1 {

  public static void main(String[] args) throws IOException {
    Reader reader = new InputStreamReader(JsonToJava1.class
        .getResourceAsStream("Server1.json"));

    Gson gson = new GsonBuilder().create();
    Person1 p = gson.fromJson(reader, Person1.class);
    System.out.println(p);

    reader.close();
  }
}
This should produce: Albert Attard - Malta. Gson parsed the JSON object and created an instance of the Person1 class.

Nested JSON Objects

Let's take the above example one step further and include a nested object as illustrated in the following JSON code fragment.

{
  NAME:"Albert Attard",
  P_LANGUAGE:"Java",
  LOCATION:"Malta",
  EXAM: {
    SUBJECT:"Programming",
    GRADE:4.5
  }
}
Here we have an EXAM field which is made from two other fields: SUBJECT and GRADE. Likewise, we need to modify the Person1 class defined above to include the EXAM field and create a new Java class to represent the SUBJECT and GRADE fields.

We first create the new class that will represent the nested object. As we discussed before, the class name is irrelevant but the fields' names must match those define in JSON.


package com.albertattard.examples.gson.part1_3;

public class Exam1 {
  private String SUBJECT;
  private double GRADE;

  // Getters and setters are not required for this example.
  // GSON sets the fields directly.

  @Override
  public String toString() {
    return SUBJECT + " - " + GRADE;
  }
}
Now we can modify the Person1 class (Person2 in the following example) and include a new field with the same name as in JSON of type Exam1 as shown next. Instead we created a new class to keep track of the progress made.

package com.albertattard.examples.gson.part1_3;

public class Person2 {

  private String NAME;
  private String LOCATION;
  private Exam1 EXAM; 

  @Override
  public String toString() {
    return NAME + " - " + LOCATION + " (" + EXAM + ")";
  }
}
Note that the changes required are minimal as Gson dynamically discovers (through reflection) the class and its fields. This article does not cover reflection. For more information about reflection please refer to: Reflection in Action.
Finally, let's test the new changes.

package com.albertattard.examples.gson.part1_3;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class JsonToJava2 {

  public static void main(String[] args) throws IOException {
    Reader reader = new InputStreamReader(JsonToJava2.class
        .getResourceAsStream("Server2.json"));

    Gson gson = new GsonBuilder().create();
    Person2 p = gson.fromJson(reader, Person2.class);
    System.out.println(p);

    reader.close();
  }
}
Instead of using person 1, we're using the new class which include the new exam field.

Conclusion

Even though it may be a new concept, JSON is very simple and straight forward. The Gson API makes it very simple to use and even though not discussed here, it provides a great deal of flexibility.

For more GSON examples please visit the second part (part-2), where we explore more complex examples and discuss how to use the GSON deserializer to take full control over the deserialization process.

27 comments:

  1. Could you please help me out creating bit.ly encoder. i have tried doing it but getting NullPointerException

    ReplyDelete
  2. Yes sure. You can post the example here or email it to me at: albertattard@gmail.com

    ReplyDelete
  3. Hi, I am new to this and this post is really helpful. However, what if the field contains a space? (I am trying to convert json to pojo so i am calling fromJson method.

    ReplyDelete
  4. I'm not sure I follow you, but fields (variables in general) in Java cannot contain spaces. If you want email me you code and I'll see if I can help you. My email address is albertattard@gmail.com

    ReplyDelete
  5. Very nice article explaining Gson in simple words. Indeed Gson is very easy and flexible to use.

    ReplyDelete
  6. Good and understandable article about GSon! Thank you...

    Best regards,
    Kai

    ReplyDelete
  7. Is there any way to input a JSON structure and spit out the equivalent representative JAVA Pojo?

    ReplyDelete
  8. Do you mean like a class Generator from a give JSON? If that's your question, I don't know. Google may here you there.

    ReplyDelete
  9. Good and easy article even for a basic programmer.

    ReplyDelete
  10. Hi All,

    My scenario is to add list of objects in Gson object. Can anyone help me out of this.

    ReplyDelete
  11. Do you need to convert a list of objects into JSON? Can you provide more information please?

    ReplyDelete
  12. what if the web service returns more than one object ?

    ReplyDelete
    Replies
    1. How come these JSON objects are not returned as an array?

      Delete
  13. hello, what does the GSON Java object look like to ingest a JSON array. is the java object an array or a parametrized list. tx

    ReplyDelete
    Replies
    1. Can you please elaborate your question and provide an example?

      Delete
  14. Hi, what can I do, if I have a json like this?

    {
    "term" : {
    "PrincipalTranslations" : {
    "zero" : {
    termine:"casa",
    traduzione:"home"
    }
    "1" :{
    termine:"testa",
    traduzione:"head"
    }
    "2" :{
    termine:"dito",
    traduzione:"finger"
    }
    }
    }
    }

    ReplyDelete
    Replies
    1. The field "term" is an object that has the field "PrincipalTranslations". This is an object which has three fields: "zero", "1", and "2". These three are of the same type (object that has two fields: "termine" and "traduzione"). But I think you know this already.

      Here is your challenge. Variables in Java cannot start with an number, thus you cannot have variables 1 and 2. To go about this problem you need to write your own GSON deserializer. I'm planning to write an article about this hopefully by the end of this week as this is the most popular page in my small blog.

      Please have a look at the official GSON site: https://sites.google.com/site/gson/gson-user-guide for more information about how you can do this. I will try my best to write an article about that soon. Should you need further help, email me and I will see what I can do.

      Delete
    2. The simpliest to be done in this case is to replace
      "1": {
      with
      "_1": {

      and create class
      public static class EntriesCollection
      {
      Entry _0;
      Entry _1;
      Entry _2;
      Entry _3;
      Entry _4;
      Entry _5;
      Entry _6;
      Entry _7;
      }
      which will be holding values.
      Other classes may look like this
      public static class Entry
      {
      TermObject OriginalTerm;
      TermObject FirstTranslation;
      String Note;
      }

      public static class TermObject
      {
      String term;
      String POS;
      String sense;
      String usage;

      }

      Delete
    3. I recommend the use of custom deserialization instead. See the following link for further information about how to do this:
      https://sites.google.com/site/gson/gson-user-guide#TOC-Custom-Serialization-and-Deserialization

      Delete
  15. but, if I had instead of "1", "2" etc names like "zero", "one", "two" etc... I'd have always an error, because "PrincipalTranslations" has 3 (or more) sub item...

    (anyway to solve the problem of number maybe, maybe I could create a parser number --> letter)

    ReplyDelete
    Replies
    1. That should not be a problem. A class or a JSON object can have more than one field. What kind of error do you have? Can you paste it here or email it tome at albertattard@gmail.com.

      Delete
  16. Hi,

    Good article. I have a bunch of json objects in a file. How do I take care of it. I can email the file if needed.

    Thanks

    ReplyDelete
    Replies
    1. It depends on how the file is structured. I would go for custom serielazation. I've added a link in one of the above replies.

      Delete
  17. I believe JavaCreed has co-opted your blog post. http://www.javacreed.com/simple-gson-example/

    ReplyDelete
    Replies
    1. Thank you for noticing.

      Please note that both sites are mine and I'm migrating from this blog to the javacreed.com website. Also, I've added a message that the beginning of this article saying that this page has moved to javacreed.

      Delete
  18. I wish people would write these articles for java beginners. Leaving out code, no matter how insignificant it may seem to the author may mean the complete misunderstanding for beginners like myself

    ReplyDelete