Jackson

Jackson vs Gson: A Deep Dive

This is a tutorial about Jackson vs Gson. Specifically, we will do a quick comparison of the libraries Gson and Jackson for serialization of a Java object to its JSON representation and deserialization of JSON string back to an equivalent Java object. We will talk about the benefits of each and see which library to use when.

1. Maven Dependencies

Firstly, let’s grab the maven dependencies and add to the classpath before we start working on this tutorial.

1.1. Gson

In the following code snippet, we’ll have a look at the maven dependency for Gson.

Gson Maven Dependencies

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

The latest version of the gson library is available here.

1.2. Jackson

The following code snippet shows the maven dependency for Jackson.

Jackson Maven Dependencies

		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.11.0</version>
		</dependency>

To get the latest version of the jackson library, click here.

2. Model Classes

We’ll be using the following entity classes for demonstrating serialization and deserialization operations with Gson & Jackson.

Employee.java

public class Employee {

	private int id;
	private String name;
	private Date date;
	private List<Task> tasks;

    // default constructor
    // parametrized constructor
    // getters , setters 
}

Task.java

public class Task {

	private int id;
	private List<String> tags;

    // default constructor
    // parametrized constructor
    // getters , setters 
}

Let’s define a method to get an instance of the Employee class to be used throughout this tutorial.

getEmployee()

	private static Employee getEmployee() {
		Task task1 = new Task(1, Arrays.asList("Java", "Python", "Go"));
		Task task2 = new Task(2, Arrays.asList("OAuth", "OIDC", "SAML"));
		Employee employee = new Employee(1, "Andy",
			Arrays.asList(task1, task2), new Date());
		return employee;
	}

3. Serialization

Serialization is the process of converting a Java object to its JSON representation. Let’s see a serialization example using the libraries Gson and Jackson and note the differences.

3.1. Gson

Let’s begin with a simple serialization example using the Gson library.

Serialization With Gson

	private static Gson gson = new GsonBuilder()
		.setPrettyPrinting().create();

	public String entityToJson(Employee employee) {
		String jsonString = gson.toJson(employee);
		System.out.println("[GSON] Employee As JSON String: "
			+ jsonString + "\n");
		return jsonString;
	}

3.2. Jackson

In this section, we will use the Jackson library to demonstrate a serialization operation.

Serialization With Jackson

	public String entityToJson(Employee employee) {
		String jsonString = null;
		try {
			jsonString = objectMapper.writerWithDefaultPrettyPrinter()
					.writeValueAsString(employee);
			System.out.println("[JACKSON] Employee As JSON String: "
				+ jsonString + "\n");
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		return jsonString;
	}

Serialization Output

[GSON] Employee As JSON String: {
  "id": 1,
  "name": "Andy",
  "date": "Jul 4, 2020, 8:43:58 PM",
  "tasks": [
    {
      "id": 1,
      "tags": [
        "Java",
        "Python",
        "Go"
      ]
    },
    {
      "id": 2,
      "tags": [
        "OAuth",
        "OIDC",
        "SAML"
      ]
    }
  ]
}

[JACKSON] Employee As JSON String: {
  "id" : 1,
  "name" : "Andy",
  "date" : 1593875638646,
  "tasks" : [ {
    "id" : 1,
    "tags" : [ "Java", "Python", "Go" ]
  }, {
    "id" : 2,
    "tags" : [ "OAuth", "OIDC", "SAML" ]
  } ]
}

The points to be noted from the above examples are:

  • We used the new GsonBuilder().setPrettyPrinting().create() statement to create a Gson instance enabled with pretty printing.
  • In Jackson, the objectMapper.writerWithDefaultPrettyPrinter() statement provides an ObjectWriter for pretty printing.
  • The Jackson ObjectMapper by default serializes the Date object as a long epoch value. Contrarily, Gson by default serializes Date as a string.

4. Deserialization

Deserialization is the process of converting a JSON string back to its POJO instance.

We will use the JSON string output from the previous serialization example to demonstrate the following deserialization operations.

4.1. Gson

Let’s see an example to run through the standard Gson deserialization process.

Deserialization With Gson

	public void jsonToEntity(String employeeJSON) {
		Employee employee = gson.fromJson(employeeJSON, Employee.class);
		System.out.println("[GSON] Employee: " + employee);
	}

4.2. Jackson

Next, let’s take a look at the standard behavior of the Jackson API for deserialization.

Deserialization With Jackson

	public void jsonToEntity(String employeeJSON) {
		Employee employee = null;
		try {
			employee = objectMapper.readValue(employeeJSON, Employee.class);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		System.out.println("[JACKSON] Employee: " + employee);
	}

Deserialization Output

[GSON] Employee Employee [id=1, name=Andy,
date=Sat Jul 04 20:47:16 IST 2020,
tasks=[Task [id=1,tags=[Java, Python, Go]],
Task [id=2,tags=[OAuth, OIDC, SAML]]]]

[JACKSON] Employee Employee [id=1, name=Andy,
date=Sat Jul 04 20:47:16 IST 2020,
tasks=[Task [id=1, tags=[Java, Python, Go]],
Task [id=2, tags=[OAuth, OIDC, SAML]]]]

The Deserialization operation prints exactly the same Java object for both Gson and Jackson libraries.

The points to be noted from the above examples are:

  • For either of the libraries, the property names in the JSON object must correlate with the Java entity field names. If the names do not match, the behavior is as follows:
  • As per the Javadoc of the class GsonBuilder, the Date serialization & deserialization operations ignore the time-zone information. Therefore, any such time-zone information present in the JSON object shall be ignored.

5. Custom Serialization

Often, it is required to override the default behavior of a library for serialization. In this section, we’ll see how to create and use a custom JSON serializer.

5.1. Gson

Let’s define a custom serializer that modifies the name of the properties in the target JSON string. Also, we’ll alter the Date representation by using a custom SimpleDateFormat instance.

CustomGSONSerializer.java

public class CustomGSONSerializer implements JsonSerializer {

	private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yy");

	@Override
	public JsonElement serialize(Employee employee, Type typeOfSrc,
			JsonSerializationContext context) {
		JsonObject employeeDetails = new JsonObject();
		JsonObject employeeJSONObj = new JsonObject();
		employeeJSONObj.addProperty("<id>Employee</id>",
			employee.getId());
		employeeJSONObj.addProperty("<name>Employee</name>",
			employee.getName());
		employeeJSONObj.addProperty("<tasks>Employee</tasks>",
			String.join(":", employee.getTasks().get(0).getTags()));
		employeeJSONObj.addProperty("<date>Employee</date>",
			sdf.format(employee.getDate()));
		employeeDetails.add("employeeDetails", employeeJSONObj);
		return employeeDetails;
	}
}

The next step is to register our custom serializer with the GsonBuilder for the appropriate Type. Also, we’ll add the configuration to disable HTML escaping and serialize the HTML characters as is.

Custom Serialization With Gson

	public String customSerializer(Employee employee) {
		Gson customGson = gson.newBuilder().disableHtmlEscaping()
			.registerTypeAdapter(Employee.class, new CustomGSONSerializer()).create();
		String jsonString = customGson.toJson(employee);
		System.out.println("[GSON] Custom Serializer: " + jsonString + "\n");
		return jsonString;
	}

Finally, as shown above, we create the Gson instance and invoke the usual toJson method to start serialization.

5.2. Jackson

Next, let’s create a custom serializer for the Jackson ObjectMapper with the same customizations as done in the CustomGSONSerializer in the previous section.

CustomJacksonSerializer.java

public class CustomJacksonSerializer extends StdSerializer {

	private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yy");

	private static final long serialVersionUID = 1L;

	public CustomJacksonSerializer() {
		this(null);
	}

	public CustomJacksonSerializer(Class clazz) {
		super(clazz);
	}

	@Override
	public void serialize(Employee employee, JsonGenerator jsonGenerator,
	  SerializerProvider serializer)	throws IOException {
		jsonGenerator.writeStartObject();
		jsonGenerator.writeObjectFieldStart("employeeDetails");
		jsonGenerator.writeNumberField("<id>Employee</id>",
			employee.getId());
		jsonGenerator.writeStringField("<name>Employee</name>", 
			employee.getName());
		jsonGenerator.writeStringField("<tasks>Employee</tasks>",
				String.join(":", employee.getTasks().get(0).getTags()));
		jsonGenerator.writeObjectField("<date>Employee</date>",
			sdf.format(employee.getDate()));
		jsonGenerator.writeEndObject();
	}
}

The following code describes how to register our own serializer with the ObjectMapperand use it for JSON serialization operations.

Custom Serialization With Jackson

	public String customSerializer(Employee employee) {
		ObjectMapper customObjMapper = new ObjectMapper();
		SimpleModule simpleModule = new SimpleModule(
			"CustomJacksonSerializer", new Version(1, 0, 0, null, null, null));
		simpleModule.addSerializer(Employee.class,
			new CustomJacksonSerializer());
		customObjMapper.registerModule(simpleModule);
		String employeeJSON = null;
		try {
			employeeJSON = customObjMapper.writerWithDefaultPrettyPrinter()
				.writeValueAsString(employee);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		System.out.println("[JACKSON] Custom Serializer Employee: "
			+ employeeJSON + "\n");
		return employeeJSON;
	}

Let’s see the output of the execution of the previous two examples.

Custom Serialization Output

[GSON] Custom Serializer Employee: {
  "employeeDetails": {
    "<id>Employee</id>": 1,
    "<name>Employee</name>": "Andy",
    "<tasks>Employee</tasks>": "Java:Python:Go",
    "<date>Employee</date>": "04-07-20"
  }
}

[JACKSON] Custom Serializer Employee: {
  "employeeDetails" : {
    "<id>Employee</id>" : 1,
    "<name>Employee</name>" : "Andy",
    "<tasks>Employee</tasks>" : "Java:Python:Go",
    "<date>Employee</date>" : "04-07-20"
  }
}

A few points to be noted from the above output are:

  • New/modified property names introduced in the JSON string by using our own serializer.
  • Date object is now serialized based on the custom SimpleDateFormat provided.

6. Custom Deserialization

There might be scenarios where we might also have to override the default deserialization behavior.

In this section, we will define our custom deserializers, register them with their libraries, and use them for deserialization operations.

6.1. Gson

The following class CustomGSONDeSerializer attempts to parse a date by using a SimpleDateFormat object. It’ll also handle the “<>” tags in the input JSON string.

Note: The input JSON string in this example is the same as the output from the custom serialization operation in the previous section.

CustomGSONDeSerializer.java

public class CustomGSONDeSerializer implements JsonDeserializer {

	private SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yy");

	@Override
	public Employee deserialize(JsonElement jsonElement, Type typeOfSrc,
	  JsonDeserializationContext context) throws JsonParseException {
		Employee employee = new Employee();
		JsonObject jsonObject = jsonElement.getAsJsonObject()
			.get("employeeDetails").getAsJsonObject();
		int empId = jsonObject.get("<id>Employee</id>").getAsInt();
		employee.setId(empId);
		employee.setName(jsonObject.get("<name>Employee</name>").getAsString());
		try {
			employee.setDate(sdf.parse(jsonObject.get(
				"<date>Employee</date>").getAsString()));
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return employee;
	}
}

Next, let’s register our custom deserializer with the GsonBuilder for the appropriate Type.

Note: When reading an input JSON with “<>” tags, it is not required to configure the GsonBuilder with disableHtmlEscaping(), unlike custom serialization.

Lastly, the usual fromJson method is invoked to begin the deserialization operation.

Custom Deserialization With Gson

	public void customDeSerializer(String employeeJSON) {
		Gson customGson = gson.newBuilder().registerTypeAdapter
			(Employee.class, new CustomGSONDeSerializer()).create();
		Employee employee = customGson.fromJson(employeeJSON, Employee.class);
		System.out.println("[GSON] Custom DeSerializer Employee: "
		  + employee + "\n");
	}

6.2. Jackson

In this section, we’ll create a custom deserializer for the ObjectMapper to modify its standard behaviour. The customizations are similar to the ones defined in the CustomGSONDeSerializer class.

CustomJacksonDeserializer.java

public class CustomJacksonDeserializer extends StdDeserializer {

	private static final long serialVersionUID = 1L;

	public CustomJacksonDeserializer() {
		this(null);
	}

	public CustomJacksonDeserializer(Class clazz) {
		super(clazz);
	}

	@Override
	public Employee deserialize(JsonParser jsonParser,
	  DeserializationContext deserializationContext) throws IOException {
		Employee employee = new Employee();
		JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
		JsonNode empDetailsNode = jsonNode.get("employeeDetails");
		int empId = empDetailsNode.get("<id>Employee</id>").asInt();
		employee.setId(empId);
		employee.setName(empDetailsNode.get(
			"<name>Employee</name>").asText());
		return employee;
	}
}

The following code shows how to register the custom deserializer with the ObjectMapper and use it for JSON deserialization operations.

Custom Deserialization With Jackson

	public void customDeSerializer(String employeeJSON) {
		ObjectMapper customObjMapper = new ObjectMapper();
		SimpleModule simpleModule = new SimpleModule("CustomJacksonDeserializer",
				new Version(1, 0, 0, null, null, null));
		simpleModule.addDeserializer(Employee.class, new CustomJacksonDeserializer());
		customObjMapper.registerModule(simpleModule);
		Employee employee = null;
		try {
			employee = customObjMapper.readValue(employeeJSON, Employee.class);
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		System.out.println("[JACKSON] Custom DeSerializer Employee : "
			+ employee + "\n");
	}

Let’s have a look at the output of the custom deserialization operation using Gson and Jackson libraries.

Custom Deserialization Output

[GSON] Custom DeSerializer Employee: Employee [id=1, 
name=Andy, date=Sun Jul 05 00:00:00 IST 2020, tasks=null]

[JACKSON] Custom DeSerializer Employee : Employee 
[id=1, name=Andy, date=null, tasks=null]

7. Annotations

The Gson library provides a limited set of annotations (@Expose, @Until, @Since, @SerializedName, @JsonAdapter). However, the Jackson library has extensive support for annotations.

In this section, we will discuss the @Expose annotation from the Gson library and the @JsonIgnore annotation from the Jackson API.

7.1. Gson

GsonBuilderprovides a configuration to exclude certain fields during serialization and deserialization operations.

In order to do so, the properties which we want to expose and not exclude should be marked with @Expose annotation as shown below.

Product.java

public class Product {

	@Expose
	private int id;
	@Expose
	private String name;
	@Expose
	private String type;
	private boolean launched;

Next, we create the Gson instance by calling the method excludeFieldsWithoutExposeAnnotation() on the GsonBulilder.

Also, note the use of serializeNulls() method. This overrides the default behaviour of Gson to ignore null values during serialization.

In other words, we force Gson to serialize properties with null values.

Gson Configuration For @Expose

	public void ignoreAndSerialize(Product product) {
		Gson customGson = gson.newBuilder()
			.excludeFieldsWithoutExposeAnnotation()
			.serializeNulls().create();
		System.out.println("[GSON] Ignore And Serialize: "
			+ customGson.toJson(product));
	}

7.2. Jackson

The @JsonIgnoreannotation is similar to the @Expose annotation. It is used to mark a property to be ignored from being serialized.

Unlike GsonBuilder, no additional configuration is required to work with this annotation.

Product.java

public class Product {
	private int id;
	private String name;
	private String type;
	@JsonIgnore
	private boolean launched;

Serialization Using Annotations Ouput

[GSON] Ignore And Serialize: {
  "id": 1,
  "name": "Television",
  "type": "Electronic"
}
[JACKSON] Ignore And Serialize: {
  "id" : 1,
  "name" : "Television",
  "type" : "Electronic"
}

Some observations from the above output are:

  • When using Gson the field launched is not marked with @Expose and hence is excluded from serialization.
  • In the case of Jackson, the launched property is annotated with @JsonIgnore. Therefore, it is ignored for serialization.

8. Summary

To summarise let’s see some noticeable difference between the two libraries.

Jackson vs Gson
  • The Gson library is designed for scenarios where you do not have access to the source code for adding annotations.
  • Also, it provides extensive support for Java Generics.
  • The toJson and the fromJson methods from the Gson library throw either a JsonSyntaxException or a JsonIOException which are unchecked exceptions (a subclass of RuntimeException).

Contrarily:

  • The Jackson API provides a rich support for annotation-based configuration.
  • It is the default library for serialization and deserialization operations in the Spring Boot framework.
  • The readValue and writeValue methods of the ObjectMapper class throw checked exceptions (a subclass of IOException and Exception).

To conclude, both the libraries are quite similar and are excellent options for processing JSON objects, pretty simple to use and really well documented.

9. Download the source code

All the code examples provided in this tutorial are available in a Maven project and should be easy to import and run.

Download
You can download the full source code of this example here: Jackson vs Gson: A Deep Dive

Anmol Deep

Anmol Deep is a senior engineer currently working with a leading identity security company as a Web Developer. He has 8 years of programming experience in Java and related technologies (including functional programming and lambdas) , Python, SpringBoot, Restful architectures, shell scripts, and databases relational(MySQL, H2) and nosql solutions (OrientDB and MongoDB). He is passionate about researching all aspects of software development including technology, design patterns, automation, best practices, methodologies and tools, and love traveling and photography when not coding.
Subscribe
Notify of
guest

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

0 Comments
Inline Feedbacks
View all comments
Back to top button