it-swarm.com.de

JSON-Analysefehler: Instanz von Java.time.LocalDate kann nicht erstellt werden: nein String-Argument-Konstruktor/Factory-Methode zum Deserialisieren vom String-Wert

Ich bin neu im Spring Data REST - Projekt und versuche, meinen ersten RESTful-Dienst zu erstellen. Die Aufgabe ist einfach, aber ich bin festgefahren.

Ich möchte CRUD-Vorgänge an Benutzerdaten ausführen, die in einer eingebetteten Datenbank mit der RESTful-API gespeichert sind. 

Ich kann jedoch nicht herausfinden, wie das Spring-Framework die birthData als "1999-12-15" verarbeitet und als LocalDate speichert. Die @JsonFormat-Annotation hilft nicht.

Zur Zeit bekomme ich den Fehler:

HTTP/1.1 400 
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 24 Aug 2017 13:36:51 GMT
Connection: close

{"cause":{"cause":null,"message":"Can not construct instance of Java.time.LocalDate: 
no String-argument constructor/factory method to deserialize from String value ('1999-10-10')\n 
at [Source: [email protected]; 
line: 1, column: 65] (through reference chain: ru.zavanton.entities.User[\"birthDate\"])"},
"message":"JSON parse error: Can not construct instance of Java.time.LocalDate: 
no String-argument constructor/factory method to deserialize from String value ('1999-10-10'); nested exception is com.fasterxml.jackson.databind.JsonMappingException: 
Can not construct instance of Java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value ('1999-10-10')\n 
at [Source: [email protected]; line: 1, column: 65] (through reference chain: ru.zavanton.entities.User[\"birthDate\"])"}

Wie es funktioniert, damit der Client anruft wie:

curl -i -X POST -H "Content-Type:application/json" -d "{  \"firstName\" : \"John\",  \"lastName\" : \"Johnson\", \"birthDate\" : \"1999-10-10\", \"email\" : \"[email protected]\" }" http://localhost:8080/users

speichert die Entität tatsächlich in der Datenbank.

Nachfolgend finden Sie die Informationen zu den Klassen.

Die Benutzerklasse: 

package ru.zavanton.entities;


import com.fasterxml.jackson.annotation.JsonFormat;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import Java.time.LocalDate;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    private String firstName;
    private String lastName;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
    private LocalDate birthDate;

    private String email;
    private String password;

    public long getId() {
        return id;
    }

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

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Die UserRepository-Klasse:

package ru.zavanton.repositories;

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import ru.zavanton.entities.User;

@RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends PagingAndSortingRepository<User, Long> {

    User findByEmail(@Param("email") String email);

}

Anwendungsklasse:

package ru.zavanton;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {

        SpringApplication.run(Application.class, args);

    }
}
13
zavanton

Für diese Serialisierung und Deserialisierung benötigen Sie eine Abhängigkeit von Jackson.

Fügen Sie diese Abhängigkeit hinzu:

Gradle:

compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4")

Maven:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Danach müssen Sie Jackson ObjectMapper anweisen, JavaTimeModule . Zu verwenden. Dazu Autowire ObjectMapper in der Hauptklasse und JavaTimeModule darin registrieren.

import javax.annotation.PostConstruct;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

@SpringBootApplication
public class MockEmployeeApplication {

  @Autowired
  private ObjectMapper objectMapper;

  public static void main(String[] args) {
    SpringApplication.run(MockEmployeeApplication.class, args);

  }

  @PostConstruct
  public void setUp() {
    objectMapper.registerModule(new JavaTimeModule());
  }
}

Danach, Ihr LocalDate und LocalDateTime sollten ordnungsgemäß serialisiert und deserialisiert werden.

13
SanketKD

Wie sich herausstellt, sollte man nicht vergessen, die Jacson-Abhängigkeit in die POM-Datei aufzunehmen. Dies löste das Problem für mich:

<dependency>
    <groupId>com.fasterxml.jackson.module</groupId>
    <artifactId>jackson-module-parameter-names</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
12
zavanton

Wenn das Problem aufgrund der Instant-Date-API auftritt, fügen Sie die folgenden Jackson-Attribute hinzu.

spring:
    application:
        name: data
    jackson:
serialization.write_dates_as_timestamps: false 

^ - füge dies in deine yml-Datei ein

jpa:
    open-in-view: false
0
Janki

Nun, was ich bei jedem Projekt mache, ist eine Mischung der oben genannten Optionen.

Fügen Sie zunächst die jsr310-Abhängigkeit hinzu:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Wichtiges Detail: Setzen Sie diese Abhängigkeit oben in Ihre Abhängigkeitsliste. Ich sehe bereits ein Projekt, in dem der Localdate-Fehler trotz dieser Abhängigkeit von der Datei pom.xml bestehen bleibt. Aber durch Ändern der Reihenfolge der Abhängigkeit war der Fehler verschwunden.

Richten Sie in Ihrer Datei /src/main/resources/application.yml Die Eigenschaft write-dates-as-timestamps Ein:

spring:
  jackson:
    serialization:
      write-dates-as-timestamps: false

Und erstelle eine ObjectMapper Bean wie folgt:

@Configuration
public class WebConfigurer {

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.build();
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return objectMapper;
    }

}

Nach dieser Konfiguration funktioniert die Konvertierung immer fehlerfrei auf Spring Boot 1.5.x.

Bonus: Spring AMQP Queue Konfiguration

Achten Sie bei der Arbeit mit Spring AMQP darauf, dass Sie eine neue Instanz von Jackson2JsonMessageConverter Haben (häufig beim Erstellen von SimpleRabbitListenerContainerFactory). Sie müssen die Bean ObjectMapper wie folgt übergeben:

Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter(objectMapper);

Andernfalls erhalten Sie den gleichen Fehler.

0
Dherik