使用新的 Record 类时无法反序列化

kar*_*kar 13 java spring spring-boot java-14 java-record

我想看看我是否可以用 Java 14 中的新 Record 类替换我现有的 Pojo。但无法这样做。得到以下错误:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造实例com.a.a.Post(无创建者,如默认构造,存在):无法从对象值反序列化(无基于委托或属性的创建者)

我知道错误是说记录没有构造函数,但是从我所看到的记录类在后台处理它,并且相关的 getter 也在后台设置(不完全是 getter,而是 id() title() 等等没有 get 前缀)。是不是因为 Spring 还没有采用最新的 Java 14 记录?请指教。谢谢。

我在 Spring Boot 版本 2.2.6 中执行此操作并使用 Java 14。

以下使用通常的 POJO 工作。

邮政类

public class PostClass {
    private int userId;
    private int id;
    private String title;
    private String body;

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}
Run Code Online (Sandbox Code Playgroud)

调用 rest 服务的方法,现在我正在使用上述 POJO。

public PostClass[] getPosts() throws URISyntaxException {
    String url = "https://jsonplaceholder.typicode.com/posts";
    return template.getForEntity(new URI(url), PostClass[].class).getBody();
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我切换到使用记录的位置,则会出现上述错误。

新记录类。

public record Post(int userId, int id, String title, String body) {
}
Run Code Online (Sandbox Code Playgroud)

更改方法以使用记录而不是失败。

public Post[] getPosts() throws URISyntaxException {
    String url = "https://jsonplaceholder.typicode.com/posts";
    return template.getForEntity(new URI(url), Post[].class).getBody();
}
Run Code Online (Sandbox Code Playgroud)

编辑:

尝试将如下构造函数添加到记录 Post 和相同的错误:

public record Post(int userId, int id, String title, String body) {
    public Post {
    }
}
Run Code Online (Sandbox Code Playgroud)

或者

public record Post(int userId, int id, String title, String body) {
    public Post(int userId, int id, String title, String body) {
        this.userId = userId;
        this.id = id;
        this.title = title;
        this.body = body;
    }
}
Run Code Online (Sandbox Code Playgroud)

use*_*547 13

某些 Jackson Annotations 是可能的,这会导致 Jackson 使用字段而不是 getter。仍然比 Java 14 之前的类简洁得多(没有 Lombok 或类似的解决方案)。

record Foo(@JsonProperty("a") int a, @JsonProperty("b") int b){
}
Run Code Online (Sandbox Code Playgroud)

这可能有效,因为根据https://openjdk.java.net/jeps/359

如果声明注释适用于记录组件、参数、字段或方法,则允许在记录组件上使用声明注释。适用于任何这些目标的声明注释将传播到任何强制成员的隐式声明。

另请参阅:何时使用 @JsonProperty 属性以及它的用途是什么?

也可以利用@JsonAutoDetect

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
Run Code Online (Sandbox Code Playgroud)

如果将对象映射器配置为全局使用字段可见性,则不需要类级别的此注释。

另请参阅:如何指定 jackson 仅使用字段 - 最好是全局的

例子:

public class Test {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper om = new ObjectMapper();
        System.out.println(om.writeValueAsString(new Foo(1, 2)));  //{"a":1,"b":2}
        System.out.println(om.writeValueAsString(new Bar(3, 4)));  //{"a":3,"b":4} 
    }

    record Foo(@JsonProperty("a") int a, @JsonProperty("b") int b){
    }

    @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
    record Bar(int a, int b){
    }
}
Run Code Online (Sandbox Code Playgroud)

该功能还有一个 Github 问题:https://github.com/FasterXML/jackson-future-ideas/issues/46


Cod*_*ode 1

编译器为记录生成构造函数和其他访问器方法。

就你而言,

  public final class Post extends java.lang.Record {  
  public Post(int, int java.lang.String, java.lang.String);
  public java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public int userId();
  public int id();
  public java.lang.String title();
  public java.lang.String body();
}
Run Code Online (Sandbox Code Playgroud)

在这里你可以看到 Jackson 没有需要的默认构造函数。您使用的构造函数是一个紧凑的构造函数,

public Post {
 }
Run Code Online (Sandbox Code Playgroud)

您可以将默认/无参数构造函数定义为,

public record Post(int userId, int id, String title, String body) {
    public Post() {
        this(0,0, null, null);
    }
}
Run Code Online (Sandbox Code Playgroud)

但 Jackson 使用 Getter 和 Setters 来设置值。简而言之,您不能使用 Record 来映射响应。


编辑为 PSA:自已发布的 2.12起,杰克逊可以正确序列化和反序列化记录

  • 杰克逊必须融入这些特征。它可能会出现在未来的版本中。 (3认同)
  • 由于某种原因,记录是**预览**,因此我们可以提供反馈。也许如果有足够多的人抱怨记录不遵循 JavaBean 命名约定,他们就会更改它。参见例如[这个答案](/sf/answers/4200953991/)。 (2认同)

归档时间:

查看次数:

2556 次

最近记录:

4 年,4 月 前