In this tutorial, we will explore how to ignore unknown JSON fields in Java using Jackson. We will cover the default behaviour, ignoring unknown properties on a class level, ignoring unknown properties globally, and how to deal with incomplete JSON. Additionally, we will discuss security concerns related to ignoring unknown properties using annotations.
If you’re new to handling JSON objects, you may want to check out the tutorial Master Mapping JSON Objects to Java Objects with Jackson first.
Default Behavior of Jackson: The UnrecognizedPropertyException Exception
By default, when Jackson encounters a JSON field that is not present in the corresponding Java object, it will throw a UnrecognizedPropertyException
. This is because Jackson maps the fields by matching the names of the JSON fields to the getter and setter methods in the Java object. If a field in the JSON does not match any getter or setter in the Java object, Jackson will not be able to map the field value to the object property and will throw the UnrecognizedPropertyException
.
Consider the following example JSON and Java class:
{ "name": "John", "age": 30, "email": "john@example.com", "phone": "555-555-5555" }
public class Person { 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; } }
If we try to deserialize the JSON using Jackson’s default behavior, we’ll get an UnrecognizedPropertyException
because the JSON contains properties that don’t exist in the Person
class:
ObjectMapper mapper = new ObjectMapper(); String json = "{\"name\":\"John\",\"age\":30,\"email\":\"john@example.com\",\"phone\":\"555-555-5555\"}"; Person person = mapper.readValue(json, Person.class);
This will result in the following exception:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "email" (class Person), not marked as ignorable (2 known properties: "name", "age"]) at [Source: (String)"{"name":"John","age":30,"email":"john@example.com","phone":"555-555-5555"}"; line: 1, column: 31] (through reference chain: Person["email"])
As we can see, the default behavior of Jackson is to throw a UnrecognizedPropertyException
when it encounters a JSON field that is not present in the corresponding Java object. However, we can configure Jackson to ignore unknown JSON fields to avoid this error.
Ignore Unknown JSON Properties on Class Level
Using The @JsonIgnoreProperties annotation
Jackson provides a way to ignore unknown JSON properties on a per-class basis. To ignore unknown properties for a specific class, we can annotate the class with @JsonIgnoreProperties
annotation and set the ignoreUnknown
attribute to true
.
Here’s an example:
@JsonIgnoreProperties(ignoreUnknown = true) public class User { private String name; private String email; // getters and setters }
In the example above, any unknown properties encountered during JSON deserialization for the User
class will be ignored. We simply annotate the class with @JsonIgnoreProperties
and set the ignoreUnknown
attribute to true
. This will tell Jackson to ignore any properties in the JSON that don’t have corresponding fields in the User
class.
Let’s take a look at a code example to see how this works:
String json = "{\"name\":\"John\",\"email\":\"john@example.com\",\"age\":30}"; ObjectMapper objectMapper = new ObjectMapper(); User user = objectMapper.readValue(json, User.class); System.out.println(user.getName()); // prints "John" System.out.println(user.getEmail()); // prints "john@example.com"
In the example above, we have a JSON string that contains an additional property age
that is not defined in the User
class. However, when we deserialize the JSON into the User
object, the age
property is ignored, and the name
and email
properties are correctly mapped to the corresponding fields in the User
class.
Note that the @JsonIgnoreProperties
annotation can also be used to ignore specific properties by listing them in the value
attribute. For example, if we want to ignore the age
property but still map any other unknown properties to the User
object, we can use the following code:
@JsonIgnoreProperties(value = {"age"}) public class User { private String name; private String email; // getters and setters }
In this case, only the age
property will be ignored, and any other unknown properties will be mapped to the corresponding fields in the User
object.
Considerations for Using @JsonIgnoreProperties(ignoreUnknown = true)
When using @JsonIgnoreProperties(ignoreUnknown = true)
to ignore unknown JSON properties during deserialization, it is essential to consider the security implications of this approach.
By ignoring unknown properties, you’re essentially allowing any data to be deserialized into your Java object, which could potentially lead to security vulnerabilities. An attacker could craft malicious JSON that includes unexpected properties that bypass your application’s security checks and access sensitive data or perform unauthorized actions.
For instance, let’s say you have a User class that contains sensitive information like email and password, and an attacker sends a malicious JSON payload with a new property isAdmin: true
. If you’re using @JsonIgnoreProperties(ignoreUnknown = true)
, the isAdmin
property will be ignored, and the payload will be successfully deserialized into your User object. As a result, the attacker could have elevated privileges and perform actions that regular users cannot.
To mitigate this security risk, you should always validate and sanitize the incoming JSON payload before deserialization. You can use libraries like OWASP’s Java HTML Sanitizer or the javax.validation library to enforce validation rules and sanitize the incoming data.
Additionally, consider using a whitelist approach rather than ignoring unknown properties. By explicitly defining the expected properties, you can avoid unexpected and potentially malicious data from being deserialized into your Java object. You can achieve this by using the @JsonProperty
annotation to map the JSON property names to the corresponding Java fields. Any unknown property not mapped using @JsonProperty
will cause an exception to be thrown during deserialization.
Here is an example of a User class that uses a whitelist approach:
class User { @JsonProperty("name") private String name; @JsonProperty("age") private int age; //getters, setters, and constructors }
In this approach, only properties that are explicitly mapped using @JsonProperty
will be deserialized, and any unknown properties will result in an exception. By using a whitelist approach, you can ensure that only the expected data is deserialized, reducing the risk of security vulnerabilities.
Ignore Unknown JSON Properties Globally
Using The ObjectMapper’s configure() Method
If you want to ignore unknown JSON properties globally for all classes, you can configure the ObjectMapper
instance to do so.
To ignore unknown properties globally, you can use the configure()
method on the ObjectMapper
instance and pass the DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
feature with the value of false
. This will tell the ObjectMapper
instance to ignore unknown properties for all classes during deserialization.
Here’s an example:
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; class Test { public static void main(String[] args) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); String json = "{\"name\": \"Tom\", \"age\": 25, \"city\": \"London\", \"membershipType\": \"golden\"}"; User user = objectMapper.readValue(json, User.class); System.out.println("User object: " + user); } }
In the example above, we configured the ObjectMapper
instance to ignore unknown properties globally by calling the configure()
method and passing DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
with the value of false
. This allows Jackson to ignore unknown properties for all classes during deserialization.
Note that this method should be used with caution, as it can mask errors in the JSON input data. It’s recommended to use the class-level @JsonIgnoreProperties
annotation for individual classes where possible.
When to Use the ObjectMapper’s configure() Method?
The configure()
method of the ObjectMapper
class provides a global configuration setting that affects all deserialization operations performed by the object mapper.
This method takes a DeserializationFeature
and a boolean value as parameters, and sets the specified feature on or off, respectively. One of the features that can be set using the configure()
method is DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
, which determines whether the ObjectMapper
instance should fail if there are any unknown properties in the JSON string.
There are scenarios where you might want to ignore unknown properties globally, instead of annotating each class with @JsonIgnoreProperties(ignoreUnknown = true)
. For example, when you have a large project with many classes, and you want to avoid repeating the same annotation over and over again, it’s more efficient to configure the ObjectMapper
instance once to ignore unknown properties globally.
Another scenario is when you have JSON strings with many unknown properties, and you don’t want to modify each corresponding Java class to add the @JsonIgnoreProperties(ignoreUnknown = true)
annotation.
Additionally, sometimes we don’t have access to the class code. In such cases, we can use the configure()
method of the ObjectMapper
class to globally ignore unknown properties during deserialization.
In these cases, using the configure()
method of the ObjectMapper
class to ignore unknown properties globally is a good option.
Bonus: Dealing with the case of an Incomplete JSON
Sometimes, JSON strings may not be complete, which means they don’t contain all the required fields. When deserializing such incomplete JSON strings, the ObjectMapper
class may throw a JsonMappingException
.
To handle this situation, we can use the DeserializationFeature
enumeration to configure the ObjectMapper
instance to ignore missing fields. We can use the configure()
method of the ObjectMapper
class to set the DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES
feature to false
. This will cause the ObjectMapper
instance to ignore any missing fields and continue with the deserialization process.
Here’s an example:
ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, false); String incompleteJson = "{\"name\": \"John\"}"; Person person = mapper.readValue(incompleteJson, Person.class);
In this example, we have an incomplete JSON string that only contains the name
field. Without the configure()
method call, the ObjectMapper
instance would throw a JsonMappingException
because the Person
class requires a age
field that is missing from the JSON string. However, with the configure()
method call, the ObjectMapper
instance will ignore the missing age
field and create a Person
object with only the name
fieldset.
Conclusion
In conclusion, the ObjectMapper class in Jackson provides powerful features for deserializing JSON strings into Java objects. We have seen how to handle various scenarios, including ignoring unknown properties, configuring global behaviour, and dealing with incomplete JSON strings.
By understanding these concepts and using the appropriate techniques, you can make your code more efficient, maintainable, and error-free. Don’t forget to check out the Java JSON Tutorials for more interesting content!