Core Java

Include null Value in JSON Serialization

1. Introduction

JavaScript Object Notation (JSON) is text-based data format that is easy for humans to read and write and easy for machines to parse and generate. It is widely used for APIs to exchange data between a client and server. JSON serialization is the process of converting a Java object into a JSON string. In Java, null is a keyword that indicates the object referring to nothing. In this example, I will demonstrate JSON null serialization with both Jackson and Gson libraries. Here are main reasons to include the null values in Json serialization:

  • Data Representation: including null value explicitly indicates that a value is missing or undefined, rather than omitted by mistake.
  • Consistent Structure: ensuring that all objects have the same structure can simplify data handling. Using null for missing values can help maintain consistency.
  • API Communication: using null can clarify that a field is intentionally left empty. This can prevent confusion about whether a field was forgotten or intentionally set to a blank state.
  • Database Operations: database null value can be used at queries and integrity constraints.
  • Schema Validation: specifying null can help ensure that data conforms to the expected schema with null value.
  • Backward Compatibility: using null allows new fields to be added without breaking existing systems that consume the data, as they can safely ignore or handle the null values.
  • Default Values: null can indicate a default state that needs to be populated later or a field that is optional and currently not set.
  • Explicit Absence: It explicitly signifies that the absence of value is acknowledged and intended, which can be important for business logic and application behavior.

2. Customer Management System

In this step, I will create a simple customer management system which has two model classes: customer and order. One customer may have one or more orders. The customer class has five fields:

  • id – the unique id.
  • name – the customer’s name, this is a required field.
  • email – the customer’s email address, it’s an optional field, but requires null value in Json serialization.
  • custTag – the customer’s tag. it’s an optional field and only included when it’s not null .
  • orders – a list of orders belongs to the customer. It is an optional field.

The order class has three members:

  • productName – the order’s product name. It’s optional.
  • quantity – the order’s quantity.
  • orderTag – the optional order tag.

3. JSON Null Serialization via Jackson

The Jackson library from FasterXML is the most popular library for serializing Java objects to JSON and vice-versa. By default, it includes the null values, however, the default behavior can be overwritten with the @JsonInclude annotation and ObjectMapper.setSerializationInclusion method. In this step, I will create a maven project and demonstrate the Json null serialization via a Junit test class.

3.1 Setup Maven project

In this step, I will create a pom.xml to include the Jackson libraries.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.zheng.demo</groupId>
	<artifactId>jackson-null</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		 

		<!--
		https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.17.1</version>
		</dependency>

 

		<!--
		https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-annotations</artifactId>
			<version>2.17.1</version>
		</dependency>

		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<version>5.10.2</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

Note: Include jackson-databind and jackson-annotations libraries.

3.2 Create Data Objects

In this step, I will create two POJO classes: Customer and Order based on the requirement at step 2.

Customer.java

package org.zheng.demo.data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class Customer implements Serializable {

	private static final long serialVersionUID = 5963349342478710542L;

	private String custTag;

	private String email;

	private int id;

	private String name;

	private List<Order> orders;

	public Customer() {
		super();
	}

	public Customer(String name, int id) {
		super();
		this.id = id;
		this.name = name;
	}

	public String getCustTag() {
		return custTag;
	}

	public String getEmail() {
		return email;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public List<Order> getOrders() {
		return orders;
	}

	public void setCustTag(String custTag) {
		this.custTag = custTag;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public void setId(int id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void addOrder(Order order) {
		if (this.orders == null) {
			this.orders = new ArrayList<>();
		}
		this.orders.add(order);
	}

	public void setOrders(List<Order> orders) {
		this.orders = orders;
	}

}

Note: line 9 marks @JsonInclude(JsonInclude.Include.NON_NULL) at the class level, so only non-null values are serialized for Customer objects.

Order.java

package org.zheng.demo.data;

import com.fasterxml.jackson.annotation.JsonInclude;

public class Order {

	@JsonInclude(JsonInclude.Include.NON_NULL)
	private String orderTag;

	private String productName;

	private int quantity;

	public Order() {
		super();
	}

	public Order(int quantity, String name) {
		super();
		this.quantity = quantity;
		this.productName = name;
	}

	public String getOrderTag() {
		return orderTag;
	}

	public String getProductName() {
		return productName;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setOrderTag(String orderTag) {
		this.orderTag = orderTag;
	}

	public void setProductName(String name) {
		this.productName = name;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

}

Note: line 7 has @JsonInclude(JsonInclude.Include.NON_NULL) at the field level, so non-null value of the orderTag field is included when serializing the Order objects.

3.3 Test via Jackson ObjectMapper

In this step, I will create a JacksonTest class which has five test methods.

  • test_include_null_as_default – print out the Order‘s JSON string and verify that Jackson library ObjectMapper includes null values in JSON as default.
  • test_include_null_as_default_with_exclusion – similar to test_include_null_as_default , but the orderTag field overrides the default setting with @JsonInclude(JsonInclude.Include.NON_NULL.
  • test_setSerializationInclusion_for_nonNull – verify the default includes null values overwritten by the ObjectMapper.setSerializationInclusion(Include.NON_NULL);
  • test_annotation_for_nonNull – The Customer class declares @JsonInclude(JsonInclude.Include.NON_NULL) at class level, so null values are excluded from JSON serialization.
  • test_mixed_annotation – verify the mixed @JsonInclude(JsonInclude.Include.NON_NULL) at Customer class declaration and the Order‘s field declaration.

JacksonTest.java

package org.zheng.demo;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import org.zheng.demo.data.Customer;
import org.zheng.demo.data.Order;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

class JacksonTest {
	private Customer cust = new Customer("Zheng", 30);
	private ObjectMapper ob = new ObjectMapper();
	private Order order = new Order();

	@Test
	void test_annotation_for_nonNull() {
		cust.setEmail("test@test.com");

		String jsonStr;
		try {
			jsonStr = ob.writerWithDefaultPrettyPrinter().writeValueAsString(cust);

			assertFalse(jsonStr.contains("null"),
					"Json string contains non-null due to JsonInclude.Include.NON_NULL set at class level.");

			System.out.println(jsonStr);

			Customer person2 = ob.readValue(jsonStr, Customer.class);

			assertEquals("Zheng", person2.getName());
			assertEquals(30, person2.getId());
			assertEquals("test@test.com", person2.getEmail());

		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

	}

	@Test
	void test_include_null_as_default() {	 
		String jsonStr;
		try {

			jsonStr = ob.writeValueAsString(order);
			assertTrue(jsonStr.contains("null"), "Json string contains null by Jackson default");
			assertFalse(jsonStr.contains("custTag"),
					"set JsonInclude.Include.NON_NULL on the custTag, so won't include");
			// include null element
			System.out.println(jsonStr); // {"productName":null,"quantity":0}

			Order data = ob.readValue(jsonStr, Order.class);

			assertNull(data.getOrderTag());

		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

	}

	@Test
	void test_include_null_as_default_with_exclusion() { 
		order.setOrderTag("serialize if not null");
		String jsonStr;
		try {

			jsonStr = ob.writeValueAsString(order);
			assertTrue(jsonStr.contains("null"), "Json string contains null by Jackson default");
			assertTrue(jsonStr.contains("orderTag"),
					"set JsonInclude.Include.NON_NULL on the orderTag. include as it has value.");

			// include null element
			System.out.println(jsonStr);
			// {"orderTag":"serialize if not null","productName":null,"quantity":0}

			Order data = ob.readValue(jsonStr, Order.class);

			assertEquals("serialize if not null", data.getOrderTag());

		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

	}

	@Test
	void test_mixed_annotation() {
		cust.addOrder(order);
		String jsonStr;
		try {
			jsonStr = ob.writerWithDefaultPrettyPrinter().writeValueAsString(cust);

			assertFalse(jsonStr.contains("\"email\" : null"),
					"Json string contains non-null due to JsonInclude.Include.NON_NULL set at Customer class level.");
			assertTrue(jsonStr.contains("\"productName\" : null"),
					"Json string contains non-null due to JsonInclude.Include.NON_NULL set at Customer class level.");

			System.out.println(jsonStr);

			Customer person2 = ob.readValue(jsonStr, Customer.class);

			assertEquals("Zheng", person2.getName());
			assertEquals(30, person2.getId());
	
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

	}

	@Test
	void test_setSerializationInclusion_for_nonNull() {
		String jsonStr;
		try {
			ob.setSerializationInclusion(Include.NON_NULL);
			jsonStr = ob.writeValueAsString(order);
			assertFalse(jsonStr.contains("null"),
					"Json string contains non-null when setSerializationInclusion() is configured globally");

			// only include non-null element
			System.out.println(jsonStr);// {"quantity":0}

			Order data = ob.readValue(jsonStr, Order.class);
			assertNull(data.getOrderTag());

		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}

	}

}
  • Line 17 – creates and initializes a cust object from the Customer class.
  • Line18 – creates an ObjectMapper with default setting.
  • Line 19 – creates and initializes an order object from the Order class.
  • Line 27 – serializes the cust object into Json via writeValueAsString with default pretty format.
  • Line 47 – serializes the order object into Json via writeValueAsString.
  • Line 69 – Excludes the “orderTag” field as it marks with @JsonInclude(JsonInclude.Include.NON_NULL).
  • Line 122 – overwrites the ObjectMapper setting with ob.setSerializationInclusion(Include.NON_NULL);

3.4 Demonstrate Json Null Serialization via Jackson

In this step, I will execute each test method and capture the output.

test_annotation_for_nonNull output

{
  "email" : "test@test.com",
  "id" : 30,
  "name" : "Zheng"
}

Note: serialized the non-null values as the Customer class annotated with @JsonInclude(JsonInclude.Include.NON_NULL).

test_include_null_as_default output

{"productName":null,"quantity":0}

Note: the productName is included with null value but the orderTag is excluded due to @JsonInclude(JsonInclude.Include.NON_NULL) annotation.

test_include_null_as_default_with_exclusion output

{"orderTag":"serialize if not null","productName":null,"quantity":0}

Note: the productName is included with null value and the orderTag is included as it is not null.

test_mixed_annotation output

{
  "id" : 30,
  "name" : "Zheng",
  "orders" : [ {
    "productName" : null,
    "quantity" : 0
  } ]
}

Note: the order’s productName is included with null value. but the email is excluded due to @JsonInclude(JsonInclude.Include.NON_NULL) at the Customer class level.

test_setSerializationInclusion_for_nonNull output

{"quantity":0}

Note: non-null values are serialized due to ob.setSerializationInclusion(Include.NON_NULL).

4. JSON Null Serialization via Gson

Gson is a Java library from Google that serializes and deserializes Java Objects into their JSON representation and vice-versa. By default, Gson does not include any null values in the JSON. I will demonstrate how to include Null values via GsonBuilder().serializeNulls().

4.1 Setup Maven project

In this step, I will create a pom.xml to include the Gson library.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.zheng.demo</groupId>
	<artifactId>gson-null</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
			<version>2.11.0</version>
		</dependency>

		<dependency>
			<groupId>org.junit.jupiter</groupId>
			<artifactId>junit-jupiter-api</artifactId>
			<version>5.10.2</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
</project>

4.2 Create Data Objects

This step is similar to step 3.2 but with Gson’s @Expose annotation which marks fields intended to expose so we can use excludeFieldsWithoutExposeAnnotation to customize JSON fields.

Customer.java

package org.zheng.demo.data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.google.gson.annotations.Expose;

public class Customer implements Serializable {

	private static final long serialVersionUID = 5963349342478710542L;

	private String custTag;

	@Expose
	private String email;

	@Expose
	private int id;

	@Expose
	private String name;

	private List<Order> orders;

	public Customer() {
		super();
	}

	public Customer(String name, int id) {
		super();
		this.id = id;
		this.name = name;
	}

	public void addOrder(Order order) {
		if (orders == null) {
			orders = new ArrayList<>();
		}
		orders.add(order);
	}

	public String getCustTag() {
		return custTag;
	}

	public String getEmail() {
		return email;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public List<Order> getOrders() {
		return orders;
	}

	public void setCustTag(String custTag) {
		this.custTag = custTag;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public void setId(int id) {
		this.id = id;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setOrders(List<Order> orders) {
		this.orders = orders;
	}
}

Note: line 15, 18 21 annotate @Expose so these fields will be serialized when excludeFieldsWithoutExposeAnnotation is configured.

The Order class detail.

Order.java

package org.zheng.demo.data;

public class Order {

	private String productName;

	private int quantity;

	public Order() {
		super();
	}

	public Order(int quantity, String productName) {
		super();
		this.quantity = quantity;
		this.productName = productName;
	}

	public String getProductName() {
		return productName;
	}

	public int getQuantity() {
		return quantity;
	}

	public void setProductName(String productName) {
		this.productName = productName;
	}

	public void setQuantity(int quantity) {
		this.quantity = quantity;
	}

}

4.3 Create Gson Test

In this step, I will create a GsonTest class with six methods to demonstrate Json null serialization via Gson and GsonBuilder libraries.

  • test_default_nonnull – the Gson and GsonBuilder's exclude null values as default.
  • test_serializeNulls – verify null values are serialized when serializeNulls is used.
  • test_serialize_null_with_Expose – verify null values are serialized when serializeNulls method is used and excludes these fields not marked with the @Expose annotation.

GsonTest.java

package org.zheng.demo;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;
import org.zheng.demo.data.Customer;
import org.zheng.demo.data.Order;

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

class GsonTest {

	private Customer customer = new Customer("Mary", 5);
	private Order order = new Order(1, null);

	@Test
	void test_default_nonnull() {
		// By default, Gson excludes fields with null values.
		String json = new Gson().toJson(order);

		assertFalse(json.contains("null"), "Gson default excludes null values.");
		System.out.println(json); // {"quantity":1}
		
		// using the GsonBuilder and setting the serializeNulls method.
		String jsonString = new GsonBuilder().create().toJson(customer);

		assertFalse(jsonString.contains("null"), "GsonBuilder default excludes null values.");
		System.out.println(jsonString); // {"id":5,"name":"Mary"}
	}


	@Test
	void test_serialize_null_with_Expose() {
		Gson gson = new GsonBuilder().serializeNulls().excludeFieldsWithoutExposeAnnotation().create();

		customer.setCustTag("NOT_Serialized_field");

		String jsonStr = gson.toJson(customer);
		
		assertFalse(jsonStr.contains("notIncludeInJson"), "not expose field not included");
		assertTrue(jsonStr.contains("null"), "Json string contains null when serializeNulls() is configured");
		System.out.println(jsonStr);
		// {"email":null,"id":5,"name":"Mary"}

		Customer desObj = gson.fromJson(jsonStr, Customer.class);
		assertNull(desObj.getCustTag());

	}

	@Test
	void test_serializeNulls() {
		customer.addOrder(order);

		Gson gson = new GsonBuilder().serializeNulls().create();
		String jsonString = gson.toJson(customer);

		assertTrue(jsonString.contains("custTag"),
				"not calling excludeFieldsWithoutExposeAnnotation, so ignore the @expose.");
		assertTrue(jsonString.contains("null"), "Json string contains null when serializeNulls() is configured");

		System.out.println(jsonString);
		// {"custTag":null,"email":null,"id":5,"name":"Mary","orders":[{"productName":null,"quantity":1}]}

	}
}
  • Line 16, 17 – defined a customer and order objects.
  • Line 20 – verify the Gson default setting includes non-null values.
  • Line 22 – toJson() serializes a Java object into JSON string.
  • Line 36, 37 – verify the GsonBuilder with serializeNulls and excludeFieldsWithoutExposeAnnotation() which includes nulls but excludes the fields without @Expose annotation.
  • Line 54, 57 – verify the GsonBuilder with serializeNulls() to serialize null values.

4.4 Demonstrate Json Null Serialization via Gson

In this step, I will execute each test and capture the output.

test_default_nonnull output

{"quantity":1}
{"id":5,"name":"Mary"}

Note: by Gson default, only non-null values are serialized.

test_serialize_null_with_Expose output

{"email":null,"id":5,"name":"Mary"}

Note: the email with the null value is serialized as expected.

test_serializeNulls output

{"custTag":null,"email":null,"id":5,"name":"Mary","orders":[{"productName":null,"quantity":1}]}

Note: the null email from customer and null productName from order are serialized as expected.

5. Conclusion

Serializing JSON with null values provides clarity and consistency. In this example, I demonstrated how to serialize JSON with null values with both Jackson and Gson open source libraries. The following table outlines the main difference at Null serialization between these two popular libraries.

JacksonGson
Open-sourceactive developmentmaintenance mode, no new features
Default null settinginclude null values as defaultexclude null values as default
ConfigurationsetSerializationInclusionserializeNulls
Annotation@JsonInclude(JsonInclude.Include.NON_NULL)No
Table 1, Json vs Jackson on Null Value Serialization

6. Download

These were two examples of maven projects which utilize both Jackson and Gson libraries for JSON null values serialization.

Download
You can download the full source code of this example here: Include null Value in JSON Serialization

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

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

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button