为什么 ProjectionFactory 将 null 转换为空 Optionals?

Why does ProjectionFactory convert nulls to empty Optionals?

最近我将我的项目从 Spring Boot 2.1.0 更新到 Spring Boot 2.5.6。从那时起,我看到了 JSON 序列化的差异。

区分情况 (a) 任意值、(b) 显式 null 和 (c) 没有价值 我使用java.util.Optional 和杰克逊的@JsonInclude(NON_NULL) 注释。此外,我使用 Spring Data JPA 的投影模式来定义 JSON 格式,如下所示:

public interface MyProjection {

    @JsonInclude(Include.NON_NULL)
    Optional<String> getMyAttribute();
}

这与 Spring Boot 2.1.0 完美配合。

Field value Rendered JSON
Optional.of("something") { "myAttribute": "something" }
Optional.empty() { "myAttribute": null }
null {}

截至目前(在我更新到 Spring Boot 2.5.6 之后)null 呈现为 Optional.empty():

Field value Rendered JSON
Optional.of("something") { "myAttribute": "something" }
Optional.empty() { "myAttribute": null }
null { "myAttribute": null }

我的第一个假设是杰克逊的 NON_NULL 不再与 Optional 一起正常工作。但事实并非如此。正如@Pedro 所说,它工作正常。经过一番调查后,我发现 Spring 的 ProjectionFactory 似乎处理 null 不同的值。它将 null 转换为 Optional.empty(),而它以前没有这样做。

我想要恢复以前的行为。有谁知道如何防止 Spring 将空值转换为空的 Optionals 而不是将其保留为空值?是否有新的默认配置?我没找到。

我已经使用 @JsonSerialize(include = Inclusion.NON_NULL) 解决了这个问题并且它起作用了。

经过几次搜索,我找到了感兴趣的测试用例。

以下是 jackson 项目中可选的测试用例的摘录:

public void testConfigAbsentsAsNullsTrue() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(true));

        OptionalData data = new OptionalData();
        String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
        assertEquals("{}", value);
    }

    public void testConfigAbsentsAsNullsFalse() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new Jdk8Module().configureAbsentsAsNulls(false));

        OptionalData data = new OptionalData();
        String value = mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).writeValueAsString(data);
        assertEquals("{\"myString\":null}", value);
    }

    class OptionalData {
        public Optional<String> myString = Optional.empty();
    }

单元测试可选Link:

https://github.com/FasterXML/jackson-datatype-jdk8/blob/master/src/test/java/com/fasterxml/jackson/datatype/jdk8/TestConfigureAbsentsAsNulls.java

并且有一个注释configureAbsentsAsNulls。 https://github.com/FasterXML/jackson-datatype-jdk8/blob/master/src/main/java/com/fasterxml/jackson/datatype/jdk8/Jdk8Module.java

For compatibility with older versions
     * of other "optional" values (like Guava optionals), it can be set to 'true'. The
     * default is `false` for backwards compatibility. 
public Jdk8Module configureAbsentsAsNulls(boolean state) {
    _cfgHandleAbsentAsNull = state;
    return this;
}

所以你只需要设置 configureAbsentsAsNulls(true) 来回滚你之前的状态。

有三种方法可以忽略1)处的空字段。场级 2)。 Class 3 级)。全球

@JsonInclude(Include.NON_NULL) 私有字符串用户名;

@JsonInclude(Include.NON_NULL)
 public class Book implements Comparable<Book> {
 private String title;
 private String author;
 private int price;

  public Book(String title, String author, int price) {
   this.title = title;
    this.author = author;
    this.price = price;
   }
  }


// let's create a book with author as null 
 Book book = new Book(null, null, 42);  
 ObjectMapper mapper = new ObjectMapper();   
 // configure ObjectMapper to exclude null fields whiel serializing 
 mapper.setSerializationInclusion(Include.NON_NULL);   
 String json =mapper.writeValueAsString(cleanCode); 
 System.out.println(json);

看来我不能再使用 java.util.Optional 了。 Spring 开发人员扩展了 ProjectingMethodInterceptor to convert null values into the empty representation of the used wrapper type(在我的例子中是 Optional.empty())。

这也适用于其他包装器类型:

  • com.google.common.base.Optional
  • scala.Option
  • io.vavr.control.Option