Google GSON for JSON Processing

Google GSON for JSON Processing

1 Comment

GSON is a very popular Java library for work with JSON.

JavaScript Object Notation (JSON) is a lightweight data exchange format. Like XML, JSON provides a way of representing object that is both human readable and machine processable.

In the Java ecosystem, there are several libraries that you can use to serialize Java objects to JSON, transmit the JSON data over a network, and deserialize the JSON back to Java objects. JSON is the most commonly used data exchange format on the Web.

In this post, we’ll take a look at using the GSON library, which stands for Google JSON.

The Maven POM

In order to use GSON, you need the JAR files of the GSON library in your project classpath. If you are using Maven, include the GSON dependency in your Maven POM.

The code to add the GSON dependency is this:

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

The POJO

For the sample application, let’s create a Product POJO with few fields to represent product information.

Here is the code of the Product class.

Product.java

package guru.springframework.blog.gson.domain;

import com.google.gson.annotations.Expose;
import java.math.BigDecimal;

public class Product {
  private String productId;
  private String description;
  private String imageUrl;
  private BigDecimal price;
  public Product(){}
  public Product(String productId, String description, String imageUrl, BigDecimal price) {
      this.productId = productId;
      this.description = description;
      this.imageUrl = imageUrl;
      this.price = price;
  }
  public String getDescription() { return description; }

  public void setDescription(String description) { this.description = description; }

  public String getProductId() { return productId; }

  public void setProductId(String productId) { this.productId = productId; }

  public String getImageUrl() { return imageUrl; }

  public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }

  public BigDecimal getPrice() { return price; }

  public void setPrice(BigDecimal price) { this.price = price; }

  public String getVersion() { return version; }

  public void setVersion(String version) { this.version = version; }

  @Override
  public String toString() {
      return "Product{" +
              "productId='" + productId + '\'' +
              ", description='" + description + '\'' +
              ", imageUrl='" + imageUrl + '\'' +
              ", price=" + price +
              '}';
  }
}

Converting Java Objects to JSON Representations

Before you start using GSON in your application, you need to first create an instance of Gson. There are two ways to do so:

  • Use the Gson class to create a new instance.
  • Create a GsonBuilder instance and call the create() method on it.

Use the GsonBuilder when you want to configure your Gson object. Otherwise, the Gson class will itself serve you.

Once you have a Gson object, you can call the toJson() method. This method takes a Java object and returns the corresponding JSON, as a String.

The following code shows how to covert the Product POJO into JSON.

. . .
public class GsonUtil {
  public static String simpleJson(Product product){
      Gson gson = new Gson();
      String json = gson.toJson(product);
      System.out.println(json);
      return json;
  }
. . .
}
. . .

Here is a unit test to test the simpleJSON() method.

package guru.springframework.blog.gson;
import guru.springframework.blog.gson.domain.Product;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import java.math.BigDecimal;

public class GsonUtilTest {
  private Product product;

  @Before
  public void setUp(){
      product = new Product("P01","Spring Guru Mug","http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg",new BigDecimal(18.95));
  }

  @After
  public void tearDown(){
   product=null;
  }
  @Test
  public void simpleJson() throws Exception {
      String result = GsonUtil.simpleJson(product);
      assertEquals(4, result.replace("{", "").replace("}","").split(",").length);
      assertEquals("\"productId\":\"P01\"".trim(), result.replace("{", "").replace("}","").split(",")[0].trim());
      assertEquals("\"description\":\"Spring Guru Mug\"".trim(), result.replace("{", "").replace("}","").split(",")[1].trim());
      assertEquals("\"imageUrl\":\"http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg\"".trim(), result.replace("{", "").replace("}","").split(",")[2].trim());
      assertEquals("\"price\":"+ new BigDecimal(18.95)+"".trim(), result.replace("{", "").replace("}","").split(",")[3].trim());

  }

}

Above, I am using JUnit as the test framework. If you are new to JUnit, I checkout my series of posts on Unit Testing using JUnit.

The output on running the test in IntelliJ is this.

JSON Serialization with GSON

For pretty printing of the JSON, using Gson use this code:

. . .
public static String simpleJsonWithPrettyPrinting(Product product){
  Gson gson = new GsonBuilder().setPrettyPrinting().create();
  String json = gson.toJson(product);
  System.out.println(json);
  return json;
}
. . .

In the code above, the setPrettyPrinting() method on the GsonBuilder creates the Gson instance with pretty printing enabled.

The code to test the simpleJsonWithPrettyPrinting() method is this.

. . .
@Test
public void simpleJsonWithPrettyPrinting() throws Exception {
  String result = GsonUtil.simpleJsonWithPrettyPrinting(product);
}
. . .

Here is the test output:
JSON Serialization with GSON with Preety Printing

Converting JSON Strings to Java Objects Using GSON

Gson also allows you to convert JSON data into Java objects.

Consider that you have the following JSON string:

String json = 
{
 "productId": "235268845711068312",
 "description": "Spring Framework Guru Mug",
 "imageUrl": "http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_mug-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg",
 "price": 14.00
}

You can parse this JSON string into a Product object using the fromJson() method of Gson as shown.

. . .
public static Product jsonToObject(String json){
  Gson gson = new Gson();
  Product product = gson.fromJson(json, Product.class);
  System.out.println(product);
  return product;
}
. . .
}
. . .

The first parameter of the fromJson() method is the source JSON that must be converted into Product. In our example it is the json variable of type String. The second parameter is the Product object (POJO) that will be initialized with the JSON.

Note: Ensure that the POJO to which the JSON is converted to has a no-argument constructor. This is required so that Gson can create an instance of this class.

The test code to test the jsonToObject() method is this.

. . .
@Test
public void jsonToObject() throws Exception {
  String json = "{\n" +
          "  \"productId\": \"235268845711068312\",\n" +
          "  \"description\": \"Spring Framework Guru Mug\",\n" +
          "  \"imageUrl\": \"http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_towel-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg\",\n" +
          "  \"price\": 14\n" +
          "}";
  Product product = GsonUtil.jsonToObject(json);
  assertNotNull(product);
  assertEquals("235268845711068312", product.getProductId());
  assertEquals("Spring Framework Guru Mug", product.getDescription());
  assertEquals("http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_towel-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg", product.getImageUrl());
  assertEquals(new BigDecimal(14), product.getPrice());

}
. . .

Here is test output from the above unit test:

JSON Deserialization with GSON

Excluding Fields from being Serialized or Deserialized

At times you might not want certain fields of a POJO to be serialized or deserialized. GSON allows you to exclude such fields from your POJO.

One of the several ways of excluding fields is by using the transient keyword.

If you declare any field in the Java class as transient, Gson ignores it during both serialization and deserialization.

The code to declare a field as transient is this.

private transient String version;

When you serialize the object to JSON, you will find that the version field of the POJO is not serialized.

To summarize, a field marked as transient ensures that it will neither be serialized nor deserialized.

However, at times you need more control. You might need that a field should be serialized but never deserialized, and vice-versa.

For such requirements, use the @Expose annotation of Gson. This annotation tells whether or not to include a field during either serialization, deserialization, or both. For example, using the @Expose annotation, you can tell that the version field should only be serialized, and not deserialized back. Similarly the other way around.

The @Expose annotation takes one, both, or none of the two elements: serialize and deserialize, both of which are of Boolean type.

The Product POJO fields after applying the @Expose annotation is this.

. . .
private String productId;
private String description;
@Expose (serialize = false, deserialize = true)
private String imageUrl;
private transient String version;
@Expose
private BigDecimal price;
. . .

In this code, the imageUrl field is marked with the @Expose annotation with the serialize element set to false and deserialize set to true. Therefor the imageUrl field will only be deserialized from JSON, but will not be serialized.

The price field is marked with @Expose, and therefore by default, it’s both serialize and deserialize elements are set to true. Therefore the The price field will both be serialized and deserialized.

You need to explicitly tell Gson to use the @Expose annotation like this.

. . .
public static String simpleJsonWithExposeFields(Product product){
  GsonBuilder gsonBuilder = new GsonBuilder();
  gsonBuilder.excludeFieldsWithoutExposeAnnotation().setPrettyPrinting();
  Gson gson = gsonBuilder.create();
  String json = gson.toJson(product);
  System.out.println(json);
  return json;
}
. . .

This code calls the excludeFieldsWithoutExposeAnnotation() method on the GsonBuilder object to exclude all fields without the @Expose annotation.

The test code is this:

. . .
@Test
public void simpleJsonWithExposeFields() throws Exception {
  String result = GsonUtil.simpleJsonWithExposeFields(product);
  assertEquals(1, result.replace("{", "").replace("}","").split(",").length);
  assertEquals("\"price\": "+ new BigDecimal(18.95)+"".trim(), result.replace("{", "").replace("}","").split(",")[0].trim());
}
. . .

The output of the test in IntelliJ is this.
JSON Serialization with GSON with Expose Fields

Custom Serialization and Deserialization

So far, we have converted JSON into Java object and vice versa using the default Gson implementation.

However, at times, you may want to configure the serialization and deserialization processes. For example, you might need to specifically map a particular POJO field with a JSON key having a different name.

To do so, you can use the JsonSerializer and JsonDeserializer interfaces.

Creating a Custom Serializer

Consider the following fields in a POJO that you need to serialize.

. . .
private String productId;
private String description;
private String imageUrl;
private BigDecimal price;
private transient String vendorName;
. . .

The POJO needs to be serialized to this JSON:

. . .
{
  "product-id": "168639393495335947",
  "description": "Spring Framework Guru Mug",
  "image-url": "http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_mug-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg",
   "price": 11.95
}
. . .

By default, Gson uses the field names as keys in the JSON. For example, the class has a field named productId, while the same field is represented as product-id in the required JSON.

To serialize the Java object into the expected JSON, you need to create a custom serializer class that implements the JsonSerializer interface. The code shows a custom serializer class.

package guru.springframework.blog.gson.custom;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import guru.springframework.blog.gson.domain.Product;


import java.lang.reflect.Type;
import java.math.RoundingMode;

public class CustomProductSerializer implements JsonSerializer<Product> {
    @Override
    public JsonElement serialize(Product product, Type type, JsonSerializationContext jsonSerializationContext) {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("product-id", product.getProductId());
        jsonObject.addProperty("description", product.getDescription());
        jsonObject.addProperty("image-url", product.getImageUrl());
        jsonObject.addProperty("price", product.getPrice().setScale(2, RoundingMode.HALF_UP));
        return jsonObject;
    }
}

The Type parameter of the JsonSerializer interface is the type of the object to be serialized.
Here an instance of Product is serialized.

The return type of the overridden serialize() method is a JsonElement. The JsonElement can be of four concrete types. One among them is JsonObject, a key-value pair where the value itself is a type of JsonElement.

To serialize the Product object, you need an instance of JsonElement. This example uses JsonObject. This object is populated with the fields as required in the JSON using the addProperty() method. Finally, the serialize() method returns the populated JsonObject.

To use this serializer, you need to register it through the GsonBuilder.

You can register and use the custom serializer, like this:

/*Register custom serializer of Product object*/
public static String simpleJsonWithCustomSerialization(Product product){
  GsonBuilder gsonBuilder = new GsonBuilder();
  gsonBuilder.registerTypeAdapter(Product.class, new CustomProductSerializer()).setPrettyPrinting();
  Gson gson = gsonBuilder.create();
  String json = gson.toJson(product);
  System.out.println(json);
  return json;
}

By registering the CustomProductSerializer class, you are telling Gson to use the custom serializer class whenever an object of Product class is serialized.

In this code, the registerTypeAdapter() method of GsonBuilder registers the custom serializer class, CustomProductSerializer.

The code to test the custom serializer is this.

. . .
@Test
public void simpleJsonWithCustomSerialization() throws Exception {
  String result = GsonUtil.simpleJsonWithCustomSerialization(product);
  assertEquals(4, result.replace("{", "").replace("}","").split(",").length);
  assertEquals("\"product-id\": \"P01\"", result.replace("{", "").replace("}","").split(",")[0].trim());
  assertEquals("\"description\": \"Spring Guru Mug\"".trim(), result.replace("{", "").replace("}","").split(",")[1].trim());
  assertEquals("\"image-url\": \"http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_shirt-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg\"".trim(), result.replace("{", "").replace("}","").split(",")[2].trim());
  assertEquals("\"price\": 18.95".trim(), result.replace("{", "").replace("}","").split(",")[3].trim());
}
. . .

The output on running the test is this.
Custom JSON Serialization with GSON

Creating a Custom Deserializer

Consider the following JSON that you need to deserialize into the Product object.

. . .
{
"product-id": "235268845711068312",
"description": "Spring Framework Guru Towel",
"image-url": "http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_towel-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg",
"price": 20.00
}
. . .

By default, Gson parses the JSON when it finds the fields in the object class with same name as it holds. So Gson by default would expect the fields of the Product class to be product-id, description, image-url, and price.

However, it is common to have different fields in the POJO class. In our example, the JSON has a key product-id while the productId field represents the same in the Product class. Same is with the image-url key whose corresponding field in Product is imageUrl.

As a result, the default serializer of Gson will not be able to deserialize the product-id and image-url keys to their corresponding fields in Product.

In order to parse this JSON into an object of Product class you can use a custom deserializer that implements the JsonDeserializer interface.

The code of the custom deserializer is this.

package guru.springframework.blog.gson.custom;

import com.google.gson.*;
import guru.springframework.blog.gson.domain.Product;


import java.lang.reflect.Type;
import java.math.BigDecimal;

public class CustomProductDeserializer implements JsonDeserializer<Product>{
    private String productId, description, imageUrl;
    private BigDecimal price;
    @Override
    public Product deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
        JsonObject jsonObject = jsonElement.getAsJsonObject();

       if(jsonObject.has("product-id")) {
           productId = jsonObject.get("product-id").getAsString();
       }
        if(jsonObject.has("description")) {
            description = jsonObject.get("description").getAsString();
        }
        if(jsonObject.has("image-url")) {
            imageUrl = jsonObject.get("image-url").getAsString();
        }
        if(jsonObject.has("price")) {
            price = jsonObject.get("price").getAsBigDecimal();
        }
        Product product = new Product(productId, description,imageUrl,price);
        return product;
    }
}

The JsonDeserializer interface takes a Type parameter which is the type of Java class to which the JSON will be converted to. Here it is the Product class.

The CustomProductSerializer class overrides the deserialize() method of JsonDeserializer which returns an instance of Product.

In the deserialize() method, the call to the getAsJsonObject() method converts the JsonElement passed as argument to deserialize() into JsonObject. The values within the JsonObject are themselves JsonElement which can be retrieved by their names. To convert these elements into their respective reference types, the getXXX() methods are called.

To use this deserializer, you need to register it through the GsonBuilder. The code to register and use the custom deserializer is this:

. . .
public static Product withCustomDeserialization() throws Exception{
  Product product = null;
  GsonBuilder gsonBuilder = new GsonBuilder();
  gsonBuilder.registerTypeAdapter(Product.class, new CustomProductDeserializer());
  Gson gson = gsonBuilder.create();
  try(Reader reader = new InputStreamReader(GsonUtil.class.getResourceAsStream("/json/product.json"))){
      product = gson.fromJson(reader, Product.class);
      System.out.println(product.getProductId());
      System.out.println(product.getDescription());
      System.out.println(product.getImageUrl());
      System.out.println(product.getPrice());
  }
  return product;
}
. . .

By registering the CustomProductDeserializer class you tell Gson to use the custom deserializer class whenever a JSON is deserialized to a Product type.

Line 5 registers the custom deserializer by calling the registerTypeAdapter() on the GsonBuilder.

Line 7 uses a Reader implementation of type InputStreamReader to read a JSON file, product.json that contains the JSON to be deserialized.

Line 8 calls the fromJson() method on the Gson instance to parse the JSON into a Product object.

The product.json file is this.

{
"product-id": "235268845711068312",
"description": "Spring Framework Guru Mug",
"image-url": "http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_towel-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg",
"price": 14
}

The code to test the custom deserializer is this.

. . .
@Test
public void objectWithCustomDeserialization() throws Exception {
  Product product = GsonUtil.withCustomDeserialization();
  assertNotNull(product);
  assertEquals("235268845711068312", product.getProductId());
  assertEquals("Spring Framework Guru Mug", product.getDescription());
  assertEquals("http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_mug-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg", product.getImageUrl());
  assertEquals(new BigDecimal(14), product.getPrice());
}

. . .

The output on running the test is this.

Using GSON with Spring Boot

Out of the box, Spring Boot uses Jackson as the JSON parser library. If you wish to learn more about Jackson, check out this blog post.

But you can seamlessly pull in Gson in your Java application for JSON processing. To include Gson, you need to configure the GsonHttpMessageConverter. Whenever a request hits the controller, the @RestController annotation asks Spring to directly render the object as model to the response.

Spring looks for HttpMessageConverter to convert the Java object into JSON.

Spring by default configures the MappingJackson2HttpMessageConverter for this conversion. But to use Gson you need to configure Spring Boot to use GsonHttpMessageConverter instead.

The Maven POM

To use Gson in your Spring Boot application, declare Gson in your Spring Boot Maven POM.

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

To avoid conflicts with the use of Jackson and Gson, use the annotation @EnableAutoConfiguration(exclude = { JacksonAutoConfiguration.class }) in your application class and exclude the Jackson dependency from your POM.

. . .
@SpringBootApplication
@EnableAutoConfiguration(exclude = { JacksonAutoConfiguration.class })
public class BlogPostsApplication {

  public static void main(String[] args) {
      SpringApplication.run(BlogPostsApplication.class, args);
  }
}

To exclude Jackson dependency from the spring-boot-starter-web dependency add this in your POM.

. . .
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 <exclusions>
    <exclusion>
       <artifactId>jackson-databind</artifactId>
       <groupId>com.fasterxml.jackson.core</groupId>
    </exclusion>
 </exclusions>
</dependency>
. . .

Validate the GSON Converter

To verify that our Spring Boot application is indeed using the Gson converter, let’s create a controller class with a request handler method that handles the request to the default request mapping to the root /.

. . .
@RestController
public class IndexController {
  @RequestMapping(value = "/",
          produces = { MediaType.APPLICATION_JSON_VALUE },
          method = RequestMethod.GET)
  public ResponseEntity<Product> getProduct() {

      double val = 15.00;
      Product product = new Product();
      product.setProductId("235268845711068313");
      product.setDescription("Spring Framework Guru Jug");
      product.setImageUrl("http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_jug-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg");
      product.setPrice(BigDecimal.valueOf(val));
      return new ResponseEntity<Product>(product, HttpStatus.OK);
  }
}
. . .

Let us run the application with debug enabled. Include the following property in your application.properties file to see the logging output.

logging.level.org.springframework=DEBUG

When you access the application from the browser, the console output is this:

2017-09-03 17:15:54.951 DEBUG 1360 --- [nio-8080-exec-1] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [Product{productId='235268845711068313', description='Spring Framework Guru Jug', imageUrl='http://springframework.guru/wp-content/uploads/2015/04/spring_framework_guru_jug-rf412049699c14ba5b68bb1c09182bfa2_8nax2_512.jpg', price=15.0}] as "application/json" using [org.springframework.http.converter.json.GsonHttpMessageConverter@1fac59e9]

As you can see, the Spring Framework is using GSON’s GsonHttpMessageConverter to convert Product to JSON instead of the default Jackson library.

Summary

As you can see in this post, Spring Boot is very flexible in allowing JSON parsers, like GSON to be plugged in for parsing JSON.

Gson is a complete JSON parser with extensive support of Java Generics. Gson supports custom representations for objects and arbitrarily complex objects having deep inheritance hierarchies.

In terms of usage, I found Gson to be fairly simple. The API is intuitive and with minimal changes to source code, you can address complex serialization and deserialization requirements of JSON to and from Java objects.

About jt

    You May Also Like

    One comment

    1. October 21, 2017 at 8:39 pm

      How do you find GSON for handling things like Enums? I’ve personally had issues with it used in some libraries, like Searchbox.io’s Jest Elasticsearch client since my projects generally use Jackson for everything. What are some ways to get them to play nicely together when needing to rely on both? They have different serialization strategies that can be conflicting depending on configuration, and managing more configuration seems like a hassle.

      Reply

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.