英文:
Spring Boot - Jersey Client - Jackson Cannot construct instance of `java.time.Instant`
问题
使用Jersey Client 2.28,我试图接收一个包含以下数据的DTO:
{
"entityId": "0de46a94-bc95-437f-9eca-dcfd60f7aed3",
"productId": "9c328c45-deba-4b84-8fb4-0d2aea885fc0",
"status": "ACTIVE",
"startDatetime": "2020-05-12T08:54:23Z",
"endDatetime": null
}
在接收时,我收到以下错误:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: 无法构造 `java.time.Instant` 的实例(没有构造器,如默认构造函数,存在):没有从字符串值('2020-05-12T08:54:23Z')反序列化为String-argument constructor/factory method to deserialize from String value ('2020-05-12T08:54:23Z')
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 190] (through reference chain: com.fundraiser.dto.OfferDto["startDatetime"])
我知道我应该能够在没有大量自定义序列化程序或适配器的情况下检索这个数据。通过谷歌搜索,似乎已经有其他类似的问题,但没有一个解决了我的问题:
我尝试了这些Maven导入的各种组合,但没有效果:
<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>
如果我添加以下Jersey Media Moxy依赖,我确实可以获取我的对象,但是任何日期都会被编组为null:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.28</version>
</dependency>
我正在使用Spring Boot 2.2.0,我的pom.xml如下所示:
<!-- 在这里放置你的 pom.xml 配置 -->
有什么明显的遗漏吗?我只是觉得这不应该是一个像我遇到的问题那么大的问题。
用于检索数据的代码:
// 在这里放置你的代码
更新:添加了DTO类:
// 在这里放置你的代码
英文:
Using Jersey Client 2.28 I am trying to receive a DTO containing the following data:
{
"entityId": "0de46a94-bc95-437f-9eca-dcfd60f7aed3",
"productId": "9c328c45-deba-4b84-8fb4-0d2aea885fc0",
"status": "ACTIVE",
"startDatetime": "2020-05-12T08:54:23Z",
"endDatetime": null,
}
When receiving it, I get the following error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.Instant` (no Creators, like default construct, exist): no String-argument constructor/factory method to deserialize from String value ('2020-05-12T08:54:23Z')
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 190] (through reference chain: com.fundraiser.dto.OfferDto["startDatetime"])
I know I should be able to retrieve this back without a ton of custom serializers or adapters. Upon googling it appears there's been other close issues to this, but none of them resolve my issue:
<https://stackoverflow.com/questions/27952472/serialize-deserialize-java-8-java-time-with-jackson-json-mapper>
<https://stackoverflow.com/questions/45863678/json-parse-error-can-not-construct-instance-of-java-time-localdate-no-string-a>
I've tried adding combinations of these maven imports to get this to work, but nothing:
<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>
If I add the following jersey media moxy, I do get my object back, but any dates are marshalled as null:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.28</version>
</dependency>
I am using Spring Boot 2.2.0 and my pom.xml looks like the following:
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
</parent>
<groupId>com.test</groupId>
<artifactId>test-webapp</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>test-webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.28</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.inject</groupId>
<artifactId>jersey-hk2</artifactId>
<version>2.28</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>test-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
</dependencies>
<properties>
<java.version>11</java.version>
</properties>
<build>
<finalName>test-webapp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<defaultGoal>install</defaultGoal>
</build>
</project>
Is there something I'm blatantly missing? I just feel like this shouldn't be as big of a problem as it has been.
Code used to retrieve data:
public TestClient() {
this.client = ClientBuilder.newBuilder().register("application/json, */*").build();
}
private WebTarget webTarget() {
return client
.target(baseUri)
.path("api")
.path("v1");
}
private WebTarget entityTarget() {
return webTarget()
.path("entity");
}
public DataDto getEntityById(@NonNull UUID entityId) {
// Error occurs here
return entityTarget()
.path(entityId.toString())
.request(MediaType.APPLICATION_JSON)
.get(DataDto.class);
}
UPDATE: Added Dto class:
package com.test.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Data
@Accessors(chain = true)
public class DataDto {
private UUID entityId;
private UUID productId;
private String status;
private Instant startDatetime;
private Instant endDatetime;
}
答案1
得分: 0
- 首先,问题中共享的 JSON 不是有效的 JSON。移除
"endDatetime": null
后面的,
。 - 在你的
DataDto
类上添加@NoArgsConstructor
注解。 - 注册 ObjectMapper 来禁用默认情况下将
Instant
序列化/反序列化为时间戳的行为。
对我有效的示例代码。我知道你是通过客户端进行操作的,但我认为这应该对你有所帮助。可以在创建客户端时要么传递一个自定义的对象映射器,要么获取 JSON 作为字符串并手动转换。
public class MapperInstant {
public static void main(String[] args) throws Exception {
String s = "{\n" +
" \"entityId\": \"0de46a94-bc95-437f-9eca-dcfd60f7aed3\",\n" +
" \"productId\": \"9c328c45-deba-4b84-8fb4-0d2aea885fc0\",\n" +
" \"status\": \"ACTIVE\",\n" +
" \"startDatetime\": \"2020-05-12T08:54:23Z\",\n" +
" \"endDatetime\": null\n" +
"}";
ObjectMapper objectMapper = new ObjectMapper()
.findAndRegisterModules()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
DataDto dataDto = objectMapper.readValue(s, DataDto.class);
System.out.println(dataDto);
}
@Data
@NoArgsConstructor
@Accessors(chain = true)
public static class DataDto {
private UUID entityId;
private UUID productId;
private String status;
private Instant startDatetime;
private Instant endDatetime;
}
}
英文:
- Firstly, the json shared in the question is not a valid json. Remove
,
after"endDatetime": null
- Add
@NoArgsConstructor
annotation to yourDataDto
class. - Register ObjectMapper to disable serialization/deserialization of
Instant
as timestamp, which it does by default.
Sample code which works for me. I know you are doing via the client but I think this should help you. Either pass a custom object mapper to the client when creating or get the JSON as a string and convert manually.
public class MapperInstant {
public static void main(String[] args) throws Exception {
String s = "{\n" +
" \"entityId\": \"0de46a94-bc95-437f-9eca-dcfd60f7aed3\",\n" +
" \"productId\": \"9c328c45-deba-4b84-8fb4-0d2aea885fc0\",\n" +
" \"status\": \"ACTIVE\",\n" +
" \"startDatetime\": \"2020-05-12T08:54:23Z\",\n" +
" \"endDatetime\": null\n" +
"}";
ObjectMapper objectMapper = new ObjectMapper()
.findAndRegisterModules()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
DataDto dataDto = objectMapper.readValue(s, DataDto.class);
System.out.println(dataDto);
}
@Data
@NoArgsConstructor
@Accessors(chain = true)
public static class DataDto {
private UUID entityId;
private UUID productId;
private String status;
private Instant startDatetime;
private Instant endDatetime;
}
}
答案2
得分: 0
尝试注册Jackson配置提供程序以在Jaxrs客户端中序列化jsr310对象,参见此处。
目前,我找不到与您的要求完全相同的示例,但是Jersey、RestEasy和Apache CXF都有各自的客户端注册机制,您应该查阅其文档的详细信息。
更新:我刚刚更新了我的一个Spring/Jersey示例,演示了如何解决这个问题,查看测试代码,注册一个自定义的Jackson上下文解析器来处理使用Spring配置的Jackson ObjectMapper
的Java 8日期时间,然后可以使用IOS格式序列化/反序列化Java 8日期时间对象。
英文:
Try to register Jackson config provider to serialize jsr310 objects in Jaxrs client, check here.
Currently, I can NOT find an example exact same with your requirements, but Jersey, rest easy, and Apache CXF have their own client registration mechanism, you should consult the details of their documentation.
Update: I just updated one of my Spring/Jersey examples to demo how to resolve this, check the testing codes, register a custom Jackson context resolver to handle the Java 8 Datetime using the Spring configured Jackson ObjectMapper
, then Java 8 date-time object can be serialized/deserialized using IOS format.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论