Core Java

Jackson Field Absent vs Null Difference

When working with JSON data in Java applications, distinguishing between absent fields and fields with null values is a common challenge. Jackson, one of the most popular JSON processing libraries for Java, provides several mechanisms to handle this distinction. This article explores the difference between absent fields and null values in Jackson, along with practical implementation strategies.

The key difference is subtle but important: a null field explicitly indicates the absence of a value, while an absent field means the property itself isn’t included in the JSON structure. Handling these distinctions correctly can make your application more robust and your APIs more precise.

1. Understanding the Difference Between Null and Absent Fields

When working with JSON data, there’s a fundamental distinction that’s often overlooked: the difference between a null field and an absent field. In JSON, these represent two distinct states:

  • Null field: The field is explicitly included in the JSON with a null value.
1
{ "username": "jane", "email": null }
  • Absent field: The field is not included in the JSON structure at all.
1
{ "username": "bob" }

While they might seem similar, they can have different semantic meanings in your application. A null value might indicate “this field exists but has no value,” while an absent field might mean “this field is not applicable.”

2. Setting Up the Project

To follow along with this tutorial, create a simple Java project with Jackson dependencies. If you are using Maven, add the following dependencies to your pom.xml:

1
2
3
4
5
6
7
<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.18.0</version>
    </dependency>
</dependencies>

Next, let’s explore different ways to distinguish between absent fields and null values.

3. Default Jackson Behavior for Missing Fields and null

Before customizing Jackson’s behavior, let’s examine its default handling of missing fields and null values during deserialization. By default, Jackson treats missing fields and fields explicitly set to null in the same way. Let’s see an example.

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
public class DefaultJacksonBehaviour {
 
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
 
        String jsonWithNull = "{\"name\":null,\"age\":20}";
        String jsonWithoutField = "{\"age\":20}";
 
        User userWithNull = objectMapper.readValue(jsonWithNull, User.class);
        User userWithoutField = objectMapper.readValue(jsonWithoutField, User.class);
 
        System.out.println("User with null name: " + userWithNull);
        System.out.println("User without name field: " + userWithoutField);
    }
 
    static class User {
 
        private String name;
        private int age;
 
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
 
        public int getAge() {
            return age;
        }
 
        public void setAge(int age) {
            this.age = age;
        }
 
        @Override
        public String toString() {
            return "User{" + "name=" + name + ", age=" + age + '}';
        }
 
    }
}

Output:

1
2
User with null name: User{name=null, age=20}
User without name field: User{name=null, age=20}

Explanation

  • The first JSON ({"name":null,"age:20"}) explicitly sets name to null, so Jackson assigns null to the name field.
  • The second JSON ({"age:20"}) omits the name field entirely. However, since Jackson does not distinguish between missing fields and null, name is still assigned null.
  • Both cases produce the same result, making it difficult to determine whether a field was missing or explicitly set to null.

To overcome this limitation, we can use Jackson’s customization features.

4. Using JsonInclude to Customize Serialization

Serialization is the process of converting a Java object into a JSON string. By default, Jackson includes all fields in the JSON output, even if they are null. However, we can use JsonInclude to control how null, empty, and default values are handled.

4.1 Excluding null Values with JsonInclude.Include.NON_NULL

Setting JsonInclude.Include.NON_NULL ensures that null fields are not included in the serialized JSON.

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
public class JsonIncludeExample {
 
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
 
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
 
        User user = new User();
        user.setName(null); // Explicitly setting to null
        user.setAge(20);
 
        String jsonOutput = objectMapper.writeValueAsString(user);
        System.out.println(jsonOutput);
    }
 
    static class User {
 
        private String name;
        private int age;
 
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
 
        public int getAge() {
            return age;
        }
 
        public void setAge(int age) {
            this.age = age;
        
         
    }
}

Output:

1
{"age":20}

The objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL) instructs Jackson to exclude null fields from serialization. In the example, since name is null, it is omitted from the JSON output, reducing payload size and preventing unnecessary null values in API responses.

4.2 Excluding Empty Collections with JsonInclude.Include.NON_EMPTY

Sometimes, we may also want to exclude empty collections, not just null values. We can achieve this using NON_EMPTY.

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
public class JsonIncludeNonEmptyExample {
     
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        User user = new User();
        user.setAge(30);
        user.setName("Mr Fish");
        user.setHobbies(List.of());
         
        String jsonOutput = objectMapper.writeValueAsString(user);
        System.out.println(jsonOutput);       
    }
     
    static class User {
         
        private List<String> hobbies;
        private String name;
        private int age;
         
        public List<String> getHobbies() {
            return hobbies;
        }
         
        public void setHobbies(List<String> hobbies) {
            this.hobbies = hobbies;
        }
         
        public String getName() {
            return name;
        }
         
        public void setName(String name) {
            this.name = name;
        }
         
        public int getAge() {
            return age;
        }
         
        public void setAge(int age) {
            this.age = age;
        }
         
    }
}

The (JsonInclude.Include.NON_EMPTY) setting ensures that empty collections like List, Set, and Map are excluded from the JSON output. In the example, although hobbies is not null, it is omitted because it is an empty list, helping to keep JSON responses cleaner and free of unnecessary empty arrays.

Output:

1
{"name":"Mr Fish","age":30}

4.3 Customizing Jackson Serialization Using Include.NON_DEFAULT

By default, Jackson includes all fields in the JSON output, even if they have default values. However, in some cases, you may want to exclude fields that still hold their default values to produce a cleaner JSON response. This can be achieved using JsonInclude.Include.NON_DEFAULT, which tells Jackson to exclude fields that are set to their default values for their data type.

The following example demonstrates this behavior:

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
public class JsonIncludeNonDefaultExample {
 
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
 
        User user = new User();
        user.setName("Mr Fish");
        user.setAge(0);
 
        String jsonOutput = objectMapper.writeValueAsString(user);
        System.out.println(jsonOutput);
    }
 
    static class User {
        private int age = 1; // Default value is set to 1
        private String name;
 
        public int getAge() {
            return age;
        }
 
        public void setAge(int age) {
            this.age = age;
        }
 
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
    }
}

Expected Output:

1
{"name":"Mr Fish"}

The (JsonInclude.Include.NON_DEFAULT) setting ensures that fields with default values are excluded from the JSON output. In this example, age has a default value of 1, and when explicitly set to 0, Jackson considers it a default value for int and excludes it from the output. However, the name field is set to "Mr Fish", which is not a default value, so it is included in the JSON.

5. Customizing Jackson Deserialization

By default, when Jackson encounters a null value for a primitive field during deserialization, it assigns the field its default value (e.g., 0 for int, false for boolean). This behavior can sometimes be undesirable, especially when you need to ensure that primitive fields are always explicitly set in the JSON input.

To enforce stricter deserialization, Jackson provides the DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES setting. When enabled, Jackson throws an exception if a null value is encountered for a primitive field instead of silently assigning the default value.

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
public class FailOnNullForPrimitivesExample {
 
    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);
 
        String jsonWithNull = "{\"age\":null}";
 
        objectMapper.readValue(jsonWithNull, User.class); // Throws exception
 
    }
 
    static class User {
 
        private int age;
 
        public int getAge() {
            return age;
        }
 
        public void setAge(int age) {
            this.age = age;
        }
 
    }
}

The objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES) setting forces Jackson to throw an exception if a null value is encountered for a primitive field. In the example, the JSON input contains {"age":null}, but since age is an int (a primitive type), Jackson does not allow null and throws a MismatchedInputException. This setting helps ensure that required fields are explicitly set in JSON input, preventing unintended default values from being assigned.

6. Conclusion

In this article, we explored how Jackson handles null and absent fields in JSON and how to customize its behavior. We covered annotations like @JsonInclude.NON_NULL, @JsonInclude.NON_ABSENT, and @JsonInclude.NON_EMPTY for cleaner serialization and FAIL_ON_NULL_FOR_PRIMITIVES for stricter deserialization. Configuring Jackson properly helps control JSON output, ensuring efficient and predictable API responses.

7. Download the Source Code

This article explored the Jackson field absent vs null difference.

Download
You can download the full source code of this example here: jackson field absent vs null difference

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
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