Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
162 views
in Technique[技术] by (71.8m points)

java - Unable to deserialize when using new Record classes

I am trying to see if I can replace my existing Pojos with the new Record classes in Java 14. But unable to do so. Getting following error:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.a.a.Post (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

I get that the error is saying the record has no constructors, but from what I see the record class takes care of it in the background and relevant getters are also set in the background (not getters exactly but id() title() and so on without the get prefix). Is it cos Spring has not adopted the latest Java 14 record yet? Please advice. Thanks.

I am doing this in Spring Boot version 2.2.6 and using Java 14.

The following works using the usual POJOs.

PostClass

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;
    }
}

Method to call rest service which works now as I am using the above POJO.

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

But if I switch to following where I am using record instead, I am getting the above error.

The new record class.

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

Changing the method to use the record instead which fails.

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

EDIT:

Tried adding constructors as follows to the record Post and same error:

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

or

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;
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It is possible with some Jackson Annotations, which cause Jackson to use fields instead of getters. Still far less verbose than a pre-Java 14 class (without Lombok or similar solutions).

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

This probably works because according to https://openjdk.java.net/jeps/359:

Declaration annotations are permitted on record components if they are applicable to record components, parameters, fields, or methods. Declaration annotations that are applicable to any of these targets are propagated to implicit declarations of any mandated members.

See also: When is the @JsonProperty property used and what is it used for?

It is also possible to make use @JsonAutoDetect

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}

If configuring the Objectmapper to use field Visibility globally, this annotation on class level is not needed.

See also: How to specify jackson to only use fields - preferably globally

Example:

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){
    }
}

There is also a Github issue for that feature: https://github.com/FasterXML/jackson-future-ideas/issues/46


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...