Google GSON for JSON Processing
1 CommentLast Updated on June 21, 2019 by Simanta
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 thecreate()
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.
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); } . . .
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:
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.
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.
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.
Josh Wood
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.