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 setsname
tonull
, so Jackson assignsnull
to thename
field. - The second JSON (
{
) omits the"age:20"
}name
field entirely. However, since Jackson does not distinguish between missing fields andnull
,name
is still assignednull
. - 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
instructs Jackson to exclude objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL)
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.
You can download the full source code of this example here: jackson field absent vs null difference