JSON解析错误:无法构造java.time.LocalDate的实例:no String-argument构造函数/工厂方法从String值反序列化

时间:2017-08-24 14:01:53

标签: json rest spring-boot jackson spring-data-rest

我是Spring Data REST项目的新手,我正在尝试创建我的第一个RESTful服务。任务很简单,但我被困住了。

我想使用RESTful API对存储在嵌入式数据库中的用户数据执行CRUD操作。

但是我无法弄清楚如何使Spring框架流程成为生成数据" 1999-12-15"并将其存储为LocalDate。 @JsonFormat注释没有帮助。

目前我收到错误:

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: org.apache.catalina.connector.CoyoteInputStream@4ee2a60e; 
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: org.apache.catalina.connector.CoyoteInputStream@4ee2a60e; line: 1, column: 65] (through reference chain: ru.zavanton.entities.User[\"birthDate\"])"}

如何使其工作,以便客户端调用如下:

curl -i -X POST -H "Content-Type:application/json" -d "{  \"firstName\" : \"John\",  \"lastName\" : \"Johnson\", \"birthDate\" : \"1999-10-10\", \"email\" : \"john@example.com\" }" http://localhost:8080/users

实际上会将实体存储到数据库中。

以下是有关课程的信息。

用户类:

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

UserRepository类:

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

}

申请类:

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

    }
}

7 个答案:

答案 0 :(得分:19)

此序列化和反序列化需要jackson依赖。

添加此依赖项:

摇篮:

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>

之后,您需要告诉Jackson ObjectMapper使用JavaTimeModule。 要做到这一点, 在主类中自动装配ObjectMapper并向其注册JavaTimeModule。

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

之后, 您的LocalDate和LocalDateTime应该被正确序列化和反序列化。

答案 1 :(得分:14)

Spring Boot 2.2.2 / Gradle:

等级(build.gradle):

implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")

实体(用户类):

LocalDate dateOfBirth;

代码:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
User user = mapper.readValue(json, User.class);

答案 2 :(得分:12)

事实证明,不应该忘记将jacson依赖包含到pom文件中。这解决了我的问题:

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

答案 3 :(得分:2)

好吧,我在每个项目上所做的事情都是上述选项的组合。

首先,添加jsr310依赖项:

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

重要详细信息:将此依赖项放在您的行为清单的顶部。我已经看到一个项目,即使依赖于pom.xml,Localdate错误仍然存​​在。但是更改礼券的顺序后,错误消失了。

在您的/src/main/resources/application.yml文件上,设置write-dates-as-timestamps属性:

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

并以此创建一个ObjectMapper bean:

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

}

按照此配置,转换始终可以在Spring Boot 1.5.x上正常进行。

奖金:Spring AMQP队列配置

使用Spring AMQP,请注意是否有新的Jackson2JsonMessageConverter实例(创建SimpleRabbitListenerContainerFactory时很常见)。您需要将ObjectMapper bean传递给它,例如:

Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter(objectMapper);

否则,您将收到相同的错误。

答案 4 :(得分:2)

我刚刚与这个搏斗了 3 个小时。我相信 Dherik 的答案(关于 AMQP 的额外材料)让我与我的答案 YMMV 相距甚远。

我在 SpringBootApplication 的对象映射器中注册了 JavaTimeModule,如下所示:

@Bean
@Primary
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.build();
    objectMapper.registerModule(new JavaTimeModule());
    return objectMapper;
}

然而,我通过 STOMP 连接的 Instants 仍然没有反序列化。然后我意识到我无意中创建了一个 MappingJackson2MessageConverter,它创建了第二个 ObjectMapper。所以我想这个故事的寓意是:你确定你已经调整了所有的 ObjectMappers 吗?在我的例子中,我用注册了 JavaTimeModule 的外部版本替换了 MappingJackson2MessageConverter.objectMapper,一切都很好:

@Autowired
ObjectMapper objectMapper;

@Bean
public WebSocketStompClient webSocketStompClient(WebSocketClient webSocketClient,
        StompSessionHandler stompSessionHandler) {
    WebSocketStompClient webSocketStompClient = new WebSocketStompClient(webSocketClient);
    MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
    converter.setObjectMapper(objectMapper);
    webSocketStompClient.setMessageConverter(converter);
    webSocketStompClient.connect("http://localhost:8080/myapp", stompSessionHandler);
    return webSocketStompClient;
}

答案 5 :(得分:0)

如果是由于即时日期API引起的问题,请添加以下杰克逊属性。

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

^-在您的yml文件中添加

jpa:
    open-in-view: false

答案 6 :(得分:0)

我有一个类似的问题,我通过进行两项更改解决了

  1. 在application.yaml文件的以下条目中添加了

    spring: jackson: serialization.write_dates_as_timestamps: false

  2. 在pojo中的两个注释下方添加

    1. @JsonDeserialize(使用= LocalDateDeserializer.class)
    2. @JsonSerialize(使用= LocalDateSerializer.class)

    示例示例

    import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; public class Customer { //your fields ... @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) protected LocalDate birthdate; }

然后以下json请求对我有用

  1. 样本请求格式为字符串

{ "birthdate": "2019-11-28" }

  1. 样本请求格式为数组

{ "birthdate":[2019,11,18] }

希望有帮助!