Java 使用新记录类时无法反序列化

Java 使用新记录类时无法反序列化,java,spring,spring-boot,java-14,java-record,Java,Spring,Spring Boot,Java 14,Java Record,我试图看看是否可以用Java14中的新记录类替换现有的POJO。但我们不能这样做。获取以下错误: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法 构造com.a.a.Post的实例(无创建者,如默认值 构造,存在):无法从对象值(无委托)反序列化- (或基于属性的创建者) 我得到的错误是记录没有构造函数,但从我看到的记录类在后台处理它,并且相关的getter也在后台设置(不是getter,而是id()title()

我试图看看是否可以用Java14中的新记录类替换现有的POJO。但我们不能这样做。获取以下错误:

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

我得到的错误是记录没有构造函数,但从我看到的记录类在后台处理它,并且相关的getter也在后台设置(不是getter,而是id()title()等等,没有get前缀)。是不是因为Spring还没有采用最新的Java14记录?请给我一些建议。谢谢

我在SpringBootVersion2.2.6中使用Java14实现了这一点

下面使用常用的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;
    }
}
方法调用rest服务,该服务现在可以工作,因为我正在使用上述POJO

public PostClass[] getPosts() throws URISyntaxException {
    String url = "https://jsonplaceholder.typicode.com/posts";
    return template.getForEntity(new URI(url), PostClass[].class).getBody();
}
但如果我切换到使用record的地方,我就会得到上面的错误

新唱片班

public record Post(int userId, int id, String title, String body) {
}
更改方法以使用记录,但失败

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

尝试按如下方式向记录栏添加构造函数,但出现相同错误:

public record Post(int userId, int id, String title, String body) {
    public Post {
    }
}


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

就你而言

  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();
}
在这里,您可以看到没有需要的默认构造函数。您使用的构造函数是一个紧凑的构造函数

public Post {
 }
您可以将默认/无参数构造函数定义为

public record Post(int userId, int id, String title, String body) {
    public Post() {
        this(0,0, null, null);
    }
}

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



编辑为PSA:as of.

某些Jackson注释可能会导致Jackson使用字段而不是getter。仍然比Java14之前的类(没有Lombok或类似的解决方案)要简单得多

这可能是因为:

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

另见:

也可以使用
@jsonautodect

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
如果将Objectmapper配置为全局使用字段可见性,则不需要类级别上的此注释

另见:

例如:

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){
    }
}
该功能还存在Github问题:

  • 为jackson使用参数名模块(确保编译器设置了-parameters)或将`@JsonProperty(“name”)添加到记录中的每个字段
  • @JsonCreator
    添加到构造函数中。我无法判断继承是否能正常工作,因此您可能必须显式声明构造函数并对其进行注释
如果显式声明了公共访问器方法或(非紧凑)规范构造函数,那么它只有直接出现在其上的注释;没有任何内容从相应的记录组件传播到这些成员

所以加上

new ObjectMapper().registerModules(new ParameterNamesModule())
试一试

@JsonCreator record Value(String x);
或者类似的

record Value(String x) {

@JsonCreator
public Value(String x) {
this.x = x;
}
}
或者一路到

record Value(@JsonProperty("x") String x) {

@JsonCreator
public Value(@JsonProperty("x") String x) {
this.x = x;
}
}
这就是我让lombok和jackson的不可变POJO工作的方式,我不明白为什么记录不能在相同的格式下工作。我的设置是Jackson parameter names module,-parameters编译器标志,用于java8(我认为这不是像jdk9+)、构造函数上的@JsonCreator所必需的。使用此设置的真实类的示例

@Value
@AllArgsConstructor(onConstructor_ = @JsonCreator)
public final class Address {

  private final String line1;

  private final String line2;

  private final String city;

  private final String region;

  private final String postalCode;

  private final CountryCode country;
}

这是预定的杰克逊2.12


显示新的
Post
类,通过错误消息,我假设您没有无参数或默认构造函数。faik记录中的所有字段都是最终字段,这意味着它可能没有无参数/默认构造函数,这是jackson用来构建对象的。请参见上面提到的@Deadpool新的Post类->公共记录Post@123尝试添加构造函数(请参见上面的编辑部分)并获得相同的结果。我也面临同样的问题-您是否找到了解决此问题的正确方法?我已经尝试了您提到的建议选项。它抛出编译错误,说“非规范记录构造函数必须委托给另一个构造函数”。因此,简而言之,您不能使用记录映射响应。Jackson必须合并这些功能。它可能会在未来的版本中出现。记录是预览的,这是有原因的,所以我们可以提供反馈。也许如果有足够多的人抱怨记录没有遵循JavaBean命名约定,他们会改变它。例如,他们不太可能改变这一点。记录作为实现细节很有用,您以前使用
Map.Entry
作为元组。
record Value(@JsonProperty("x") String x) {

@JsonCreator
public Value(@JsonProperty("x") String x) {
this.x = x;
}
}
@Value
@AllArgsConstructor(onConstructor_ = @JsonCreator)
public final class Address {

  private final String line1;

  private final String line2;

  private final String city;

  private final String region;

  private final String postalCode;

  private final CountryCode country;
}