Jackson,JSON和API中未知字段的正确处理


设想以下情形:您拥有一个通过使用REST端点与另一个应用程序集成的应用程序。要执行序列化/反序列化,请使用著名的Jackson库,该库将Java对象神奇地转换为JSON(反序列化),反之亦然(反序列化)。晴朗的一天,突然之间,您的请求停止工作,但出现以下异常:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field

会发生什么事?异常本身告诉您:JSON中有未知属性正在执行反序列化。

解释例外 根据官方文件:

专门的JsonMappingException子类专门用于指示由于遇到无法映射到Object属性(通过getter,构造函数参数或字段)的JSON属性而引起的问题。

简而言之,只要JSON中有一个尚未映射到其java / DTO对象的属性,Jackson就会抛出此异常。

那么会发生什么呢? 您的应用程序正在使用的服务提供者在服务的返回中添加了新属性。由于您的java / DTO对象中不存在这样的属性,因此我们具有无法识别的属性,因此无法进行反序列化过程(JSON->对象)。

Ok, Sherlock! And now? 解释一下,纠正自己:只要JSON中的某个属性没有映射到其Java / DTO对象,Jackson都会抛出此异常,除非您告诉Jackson他可以忽略此类属性。

忽略Jackson的未知领域 幸运的是,有两种方法可以解决有问题的问题并避免引发异常:

  1. 用@JsonIgnoreProperties注释类(ignoreUnknown = true)
  2. 将反序列化功能FAIL_ON_UNKNOWN_PROPERTIES设置为false

@JsonIgnoreProperties(ignoreUnknown = true) 在类中添加@JsonIgnoreProperties(ignoreUnknown = true)批注将告诉Jackson在将JSON反序列化为该类中的对象时忽略未知属性。

@JsonIgnoreProperties(ignoreUnknown=true)
public class AnnotatedPersonDto {
  private String name;
  private String sex;
  // ...
}

将反序列化功能FAIL_ON_UNKNOWN_PROPERTIES设置为false 设置对象映射器将告诉Jackson在使用该对象映射器的所有反序列化中忽略未知属性。

// version 1.9 or before
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// version 2.0 or after
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

让我们去测试 对于我们的测试,我们将需要两个DTO,一个不带注释(以证明引发了异常),另一个带注释(以解决问题)。

public class UnannotatedPersonDto {
  private String name;
  private String sex;
  // ...
}
@JsonIgnoreProperties(ignoreUnknown=true)
public class AnnotatedPersonDto {
  private String name;
  private String sex;
  // ...
}

将使用下面的JSON,该JSON具有DTO未知的age属性。

{ "name": "LINUS" , "age": 18, "sex": "MALE" }

下面我们有3个测试:

@SpringBootTest
class JacksonIgnorePropertiesTests {

  private String JSON_TO_DESERIALIZE = "{ \"name\": \"LINUS\" , \"age\": 18, \"sex\": \"MALE\" }";

    @Test
    void withJsonWithUnknownAttributes_whenWithoutAnnotationOrConfiguration_thenThrownException() throws JsonMappingException, JsonProcessingException {
      ObjectMapper mapper = new ObjectMapper();
      assertThrows(UnrecognizedPropertyException.class, () -> { mapper.readValue(JSON_TO_DESERIALIZE, UnannotatedPersonDto.class); });
    }

    @Test
    void withJsonWithUnknownAttributes_whenDtoHasAnnotationJsonIgnoreProperties_thenWillDeserialize() throws JsonMappingException, JsonProcessingException {
      ObjectMapper mapper = new ObjectMapper();
      assertEquals("LINUS", mapper.readValue(JSON_TO_DESERIALIZE, AnnotatedPersonDto.class).getNome());

    }

    @Test
    void withJsonWithUnknownAttributes_whenObjectMapperIsConfigured_thenWillDeserialize() throws JsonMappingException, JsonProcessingException {
      ObjectMapper mapper = new ObjectMapper();
      mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      assertEquals("LINUS", mapper.readValue(JSON_TO_DESERIALIZE, UnannotatedPersonDto.class).getNome());
    }

}

结果: 一旦使用没有注释的类对JSON反序列化了JSON,并且没有将任何配置添加到Object Mapper中,则会引发UnrecognizedPropertyException异常。

反序列化成功,因为DTO带有用于忽略未知属性的注释。

反序列化也成功发生,因为尽管使用了不带批注的DTO来忽略未知属性,但对象映射器配置了FAIL_ON_UNKNOWN_PROPERTIES功能,该功能会忽略那些属性。

结论 那么最好的方法是什么?这取决于。

使用@JsonIgnoreProperties注释类的方法可以更好地控制哪些对象应该忽略未知字段,哪些对象不应该忽略。另一方面,开发人员可能会忘记将注释放入类中,然后可能会出现问题。

根据依赖项注入框架配置对象映射器的方法(确保在整个系统中使用相同的对象映射器),将确保整个应用程序中的异常消失,但会使开发人员对发生的演变一无所知在API中被消耗。


原文链接:http://codingdict.com/