Невозможно десериализовать при использовании новых классов записи - PullRequest
5 голосов
/ 10 апреля 2020

Я пытаюсь выяснить, могу ли я заменить мои существующие Pojos новыми классами Record в Java 14. Но не могу этого сделать. Получение следующей ошибки:

com.faster xml .jackson.databind.ex c .InvalidDefinitionException: невозможно создать экземпляр com.a.a.Post (не существует создателей, таких как конструкция по умолчанию): невозможно десериализовать из значения объекта (без создателя на основе делегатов или свойств)

Я получаю сообщение об ошибке, в котором говорится, что запись не имеет конструкторов, но, как я вижу, класс записей позаботится об этом в background и соответствующие геттеры также устанавливаются в фоновом режиме (точно не getter, а id () title () и т. д. без префикса get). Это потому, что Spring еще не принял последнюю запись Java 14? Пожалуйста посоветуй. Спасибо.

Я делаю это в Spring Boot версии 2.2.6 и использую Java 14.

Следующее работает с использованием обычных POJO.

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

Метод вызова службы отдыха, которая работает сейчас, когда я использую вышеуказанный POJO.

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

Но если я переключаюсь на следующее, где вместо этого использую запись, я получаю вышеуказанную ошибку.

Новый класс записи.

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

РЕДАКТИРОВАТЬ:

Попытка добавления конструкторов следующим образом к записи Post и та же ошибка:

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

или

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

Ответы [ 2 ]

3 голосов
/ 10 апреля 2020

Компилятор генерирует конструктор и другой метод доступа для Записи.

В вашем случае

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

Но Джексон использует Getter и Setters для установки значений. Короче говоря, вы не можете использовать запись для сопоставления ответа.

2 голосов
/ 25 апреля 2020

Это возможно с некоторыми аннотациями Джексона, которые заставляют Джексона использовать поля вместо геттеров. Все еще гораздо менее многословно, чем класс до Java 14 (без Lombok или подобных решений).

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

Это, вероятно, работает, потому что согласно https://openjdk.java.net/jeps/359:

Аннотации объявлений разрешены для компонентов записи, если они применимы к компонентам записи, параметрам, полям или методам. Аннотации объявлений, которые применимы к любой из этих целей, распространяются на неявные объявления любых уполномоченных членов.

См. Также: Когда используется свойство @JsonProperty и для чего оно используется?

Можно также использовать @JsonAutoDetect

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

Если настройка Objectmapper для использования поля Visibility глобально, эта аннотация на уровне класса не требуется.

См. Также: Как указать Джексона для использования только полей - желательно глобально

Пример:

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: https://github.com/FasterXML/jackson-future-ideas/issues/46

...