Jackson Unmarshalling JSON with Unknown Properties

1. Overview

In this article, we’re going to take a look at the unmarshalling process with Jackson 2.x – specifically at how to deal with JSON content with unknown properties.

If you want to dig deeper and learn other cool things you can do with Jackson – head on over to the main Jackson tutorial.

2. Unmarshall a JSON With Additional/Unknown Fields

JSON input comes in all shapes and sizes – and most of the time, we need to map it to predefined Java objects with a set number of fields. The goal is to simply ignore any JSON properties that cannot be mapped to an existing Java field.

For example, say we need to unmarshal JSON to the following Java entity:

public class MyDto {

    private String stringValue;
    private int intValue;
    private boolean booleanValue;

    // standard constructor, getters and setters 
}

2.1. UnrecognizedPropertyException on Unknown Fields

Trying to unmarshal a JSON with unknown properties to this simple Java Entity will lead to a com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:

@Test(expected = UnrecognizedPropertyException.class)
public void givenJsonHasUnknownValues_whenDeserializing_thenException()
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString = 
        "{"stringValue":"a"," +
        ""intValue":1," +
        ""booleanValue":true," +
        ""stringValue2":"something"}";
    ObjectMapper mapper = new ObjectMapper();

    MyDto readValue = mapper.readValue(jsonAsString, MyDto.class);

    assertNotNull(readValue);
}

This will fail with the following exception:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: 
Unrecognized field "stringValue2" (class org.maixuanviet.jackson.ignore.MyDto), 
not marked as ignorable (3 known properties: "stringValue", "booleanValue", "intValue"])

2.2. Dealing With Unknown Fields Using the ObjectMapper

We can now configure the full ObjectMapper to ignore unknown properties in the JSON:

new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

We should then be able to read this kind of JSON into a predefined Java entity:

@Test
public void givenJsonHasUnknownValuesButJacksonIsIgnoringUnknowns_whenDeserializing_thenCorrect()
  throws JsonParseException, JsonMappingException, IOException {
 
    String jsonAsString = 
        "{"stringValue":"a"," +
        ""intValue":1," +
        ""booleanValue":true," +
        ""stringValue2":"something"}";
    ObjectMapper mapper = new ObjectMapper()
      .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    MyDto readValue = mapper.readValue(jsonAsString, MyDto.class);

    assertNotNull(readValue);
    assertThat(readValue.getStringValue(), equalTo("a"));
    assertThat(readValue.isBooleanValue(), equalTo(true));
    assertThat(readValue.getIntValue(), equalTo(1));
}

2.3. Dealing With Unknown Fields at the Class Level

We can also mark a single class as accepting unknown fields, instead of the entire Jackson ObjectMapper:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MyDtoIgnoreUnknown { ... }

Now, we should be able to test the same behavior as before – unknown fields are simply ignored and only known fields are mapped:

@Test
public void givenJsonHasUnknownValuesButIgnoredOnClass_whenDeserializing_thenCorrect() 
  throws JsonParseException, JsonMappingException, IOException {
 
    String jsonAsString =
        "{"stringValue":"a"," +
        ""intValue":1," +
        ""booleanValue":true," +
        ""stringValue2":"something"}";
    ObjectMapper mapper = new ObjectMapper();

    MyDtoIgnoreUnknown readValue = mapper
      .readValue(jsonAsString, MyDtoIgnoreUnknown.class);

    assertNotNull(readValue);
    assertThat(readValue.getStringValue(), equalTo("a"));
    assertThat(readValue.isBooleanValue(), equalTo(true));
    assertThat(readValue.getIntValue(), equalTo(1));
}

3. Unmarshall an Incomplete JSON

Similarly to additional unknown fields, unmarshalling an incomplete JSON – a JSON that doesn’t contain all the fields in the Java class – is not a problem with Jackson:

@Test
public void givenNotAllFieldsHaveValuesInJson_whenDeserializingAJsonToAClass_thenCorrect() 
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString = "{"stringValue":"a","booleanValue":true}";
    ObjectMapper mapper = new ObjectMapper();

    MyDto readValue = mapper.readValue(jsonAsString, MyDto.class);

    assertNotNull(readValue);
    assertThat(readValue.getStringValue(), equalTo("a"));
    assertThat(readValue.isBooleanValue(), equalTo(true));
}

4. Conclusion

This article covered deserializing a JSON with additional, unknown properties, using Jackson.

This is one of the most common things to configure when working with Jackson since it’s often the case we need to map JSON results of external REST APIs to an internal Java representation of the entities of the API.

The implementation of all these examples and code snippets can be found in my GitHub project.

Related posts: