Ignore Unknown JSON Fields with Java Jackson

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.

Jackson: Java to JSON

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": "[email protected]",
   "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\":\"[email protected]\",\"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":"[email protected]","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\":\"[email protected]\",\"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 "[email protected]"

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!

Leave a Reply

Your email address will not be published. Required fields are marked *