1. 概述

在这个教程中,我们将探讨Jackson 2.x中的反序列化过程,特别是如何处理包含未知属性的JSON内容。

要深入了解并学习我们能用Jackson做的其他酷炫事情,可以参考Jackson主教程

2. 反序列化带有额外或未知字段的JSON

JSON输入形式多样,大多数时候我们需要将它映射到预定义的Java对象,这些对象具有固定数量的字段。目标是忽略任何无法映射到现有Java字段的JSON属性

例如,假设我们需要将JSON反序列化为以下Java实体:

public class MyDto {

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

    // standard constructor, getters and setters 
}

2.1. 对于未知字段引发UnrecognizedPropertyException

尝试将带有未知属性的JSON反序列化到这个简单的Java实体会导致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);
}

这将抛出以下异常:

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

2.2. 使用ObjectMapper处理未知字段

现在我们可以配置整个ObjectMapper,使其在JSON中忽略未知属性:

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

然后我们应该能够将这种类型的JSON读取到预定义的Java实体中:

@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. 在类级别处理未知字段

我们也可以标记单个类接受未知字段,而不是整个Jackson ObjectMapper

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

现在我们应该能够测试之前的行为。未知字段被简单地忽略,只有已知字段被映射:

@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. 反序列化不完整的JSON

类似于额外的未知字段,使用Jackson反序列化不完整的JSON(即不包含Java类所有字段的JSON)并非问题:

@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. 总结

在这篇文章中,我们讨论了使用Jackson处理带有额外、未知属性的JSON的反序列化。

在与Jackson一起工作时,这是最常见的配置之一,因为我们经常需要将外部REST API的JSON结果映射到内部API实体的Java表示

所有示例和代码片段的实现可以在我的GitHub项目中找到:GitHub项目示例