【问题标题】:JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String valueJSON 解析错误:无法构造 java.time.LocalDate 的实例:没有从字符串值反序列化的字符串参数构造函数/工厂方法
【发布时间】:2018-02-02 10:59:26
【问题描述】:

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

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

但我不知道如何让 Spring 框架将birthData 处理为“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);

    }
}

【问题讨论】:

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


【解决方案1】:

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

  1. application.yaml 文件中的条目下方添加
spring:
    jackson:
        serialization.write_dates_as_timestamps: false
  1. 在 POJO 的 LocalDate 字段中添加以下两个注释
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)

例子

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

public class Customer   {
    @JsonDeserialize(using = LocalDateDeserializer.class)
    @JsonSerialize(using = LocalDateSerializer.class)
    protected LocalDate birthdate;
}

示例请求格式:

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

示例请求格式为数组

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

【讨论】:

  • 非常感谢!你节省了我的时间!我还将这行代码添加到 pojo @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
  • 我觉得这个很干净。
  • FWIW 我发现不需要修改application.yaml,但有必要添加依赖项jackson-datatype-jsr310,并按照建议包括@JsonFormat
【解决方案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 连接的 Instant 仍未反序列化。然后我意识到我无意中创建了一个 MappingJackson2MessageConverter,它创建了第二个 ObjectMapper。所以我想这个故事的寓意是:你确定你已经调整了所有的 ObjectMapper 吗?就我而言,我将 MappingJackson2MessageConverter.objectMapper 替换为注册了 JavaTimeModule 的外部版本,一切都很好:

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

【讨论】:

    【解决方案3】:

    Spring Boot 2.2.2 / Gradle:

    Gradle (build.gradle):

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

    实体(User.class):

    LocalDate dateOfBirth;
    

    代码:

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

    【讨论】:

    • 谢谢,我尝试了很多解决方案,但都不起作用,但这个是最好的。我如何学习并获得有关该课程功能的更多详细信息?
    • yyyy-mm-dd 格式在 ISO 8601 下进行了标准化。JSR-310 指定了对 Java 8 日期和时间 API 的添加以纳入 ISO8601。上面显示的依赖项是一个补丁,用于将此功能添加到 2.85 之前的 Jackson 版本。 2.85 及更高版本支持 Java 日期和时间 API。更多信息在这里:github.com/FasterXML/jackson-modules-java8/tree/master/datetime
    • 除了这个正确的答案之外,您还可以让模块自动发现,就像手动注册它们一样。 eg: new ObjectMapper().findAndRegisterModules(); 也就是说,手动注册和自动注册不应该混为一谈,只有一个注册才会生效。 Source: FasterXML jackson-modules-java8
    【解决方案4】:

    嗯,我在每个项目上所做的都是上述选项的混合。

    首先,添加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);
    

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

    【讨论】:

      【解决方案5】:

      这个序列化和反序列化需要jackson依赖。

      添加这个依赖:

      分级:

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

      马文:

      <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 应该正确序列化和反序列化。

      【讨论】:

      • 我只需要添加 gradle 依赖来修复它
      • 这部分让它对我有用objectMapper.registerModule(new JavaTimeModule());
      • 无需注册JavaTimeModule。如果您在 Classpath 中有依赖项,它将自动注册。
      • 好像没有自动注册,我必须明确注册才能工作。
      • 您还可以让模块自动发现,就像明确注册它们一样。 eg: new ObjectMapper().findAndRegisterModules(); 也就是说,手动注册和自动注册不应该混为一谈,只有一个注册才会生效。 Source: FasterXML jackson-modules-java8
      【解决方案6】:

      事实证明,不要忘记将 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>
      

      【讨论】:

      • 即使添加了这些依赖项后它仍然无法正常工作怎么办。有什么办法可以让 Json 忽略这个参数或类似的东西?
      猜你喜欢
      • 1970-01-01
      • 2019-06-14
      • 2017-03-03
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 2019-01-21
      • 2019-12-31
      • 2017-12-23
      相关资源
      最近更新 更多