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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.zheng.demo</groupId>
    <artifactId>jackson-null</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
          
 
        <!--
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.17.1</version>
        </dependency>
 
  
 
        <!--
        <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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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

1
2
3
4
5
{
  "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

1
{"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

1
{"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

1
2
3
4
5
6
7
8
{
  "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

1
{"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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.zheng.demo</groupId>
    <artifactId>gson-null</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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

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

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

test_serialize_null_with_Expose output

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

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

test_serializeNulls output

1
{"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