简体   繁体   中英

Spring Boot - Jersey Client - Jackson Cannot construct instance of `java.time.Instant`

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:

serialize/deserialize java 8 java.time with Jackson JSON mapper

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

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;
}
  • Firstly, the json shared in the question is not a valid json. Remove , after "endDatetime": null
  • Add @NoArgsConstructor annotation to your DataDto 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;
    }
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM