Spring 引导 - Jersey 客户端 - Jackson 无法构造“java.time.Instant”的实例

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: 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"])

我知道我应该能够在没有大量自定义序列化器或适配器的情况下取回它。谷歌搜索后,似乎还有其他接近的问题,但 none 解决了我的问题:

JSON parse error: Can not construct instance of java.time.LocalDate: no String-argument constructor/factory method to deserialize from String value

我试过添加这些 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,我确实会取回我的对象​​,但任何日期都被编组为空:

<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-moxy</artifactId>
  <version>2.28</version>
</dependency>

我正在使用 Spring Boot 2.2.0,我的 pom.xml 如下所示:

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

有什么我公然遗漏的吗?我只是觉得这不应该像过去那样成为一个大问题。

用于检索数据的代码:


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

更新:添加了 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;
}
  • 首先,问题中分享的 json 不是有效的 json。在 "endDatetime": null
  • 之后删除 ,
  • @NoArgsConstructor 注释添加到您的 DataDto class。
  • 注册 ObjectMapper 以禁用 Instant 的 serialization/deserialization 作为时间戳,默认情况下它会这样做。

适用于我的示例代码。我知道你是通过客户端做的,但我认为这应该对你有帮助。创建时将自定义对象映射器传递给客户端,或者将 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;
    }
}

尝试注册 Jackson 配置提供程序以在 Jaxrs 客户端中序列化 jsr310 对象,检查 here

目前,我找不到与您的要求完全相同的示例,但是 Jersey、rest easy 和 Apache CXF 都有自己的客户端注册机制,您应该查阅他们的文档的详细信息。

更新:我刚刚更新了我的一个 Spring/Jersey 示例来演示如何解决这个问题,检查 the testing codes,注册一个自定义 Jackson 上下文解析器到使用 Spring 配置 Jackson ObjectMapper 处理 Java 8 日期时间,然后 Java 8 date-time 对象可以 serialized/deserialized 使用 IOS格式。