2017-09-22 70 views
1

我正在爲REST端點製作serivce客戶端,使用JAX-RS客戶端進行HTTP請求,並使用傑克遜對(序列化)JSON實體。爲了處理JSR-310(Java8)日期/時間對象,我將com.fasterxml.jackson.datatype:jackson-datatype-jsr310模塊作爲依賴項添加到服務客戶端,但我沒有得到它的工作。JAX-RS客戶端對Java8日期/時間的傑克遜(德)序列化

如何配置JAX-RS和/或Jackson使用jsr310模塊?

我用下面的依賴關係:

<dependency> 
    <groupId>javax.ws.rs</groupId> 
    <artifactId>javax.ws.rs-api</artifactId> 
    <version>${jax-rs.version}</version> 
</dependency> 
<dependency> 
    <groupId>com.fasterxml.jackson.core</groupId> 
    <artifactId>jackson-annotations</artifactId> 
    <version>${jackson.version}</version> 
</dependency> 
<dependency> 
    <groupId>com.fasterxml.jackson.datatype</groupId> 
    <artifactId>jackson-datatype-jsr310</artifactId> 
    <version>${jackson.version}</version> 
</dependency> 

我不想讓服務客戶端(被釋放庫)依賴於任何特定的實現 - 新澤西州一樣,所以我只能依靠JAX-RS API。運行我的集成測試我說:

<dependency> 
    <groupId>org.glassfish.jersey.core</groupId> 
    <artifactId>jersey-client</artifactId> 
    <version>${jersey.version}</version> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>org.glassfish.jersey.inject</groupId> 
    <artifactId>jersey-hk2</artifactId> 
    <version>${jersey.version}</version> 
    <scope>test</scope> 
</dependency> 
<dependency> 
    <groupId>org.glassfish.jersey.media</groupId> 
    <artifactId>jersey-media-json-jackson</artifactId> 
    <version>${jersey.version}</version> 
    <scope>test</scope> 
</dependency> 

的JAX-RS客戶端的實例化是在工廠對象來完成,如下所示:

import javax.ws.rs.Produces; 
import javax.ws.rs.client.Client; 
import javax.ws.rs.client.ClientBuilder; 

@Produces 
public Client produceClient() { 
    return ClientBuilder.newClient(); 
} 

一個典型的DTO是這樣的:

import com.fasterxml.jackson.annotation.JsonCreator; 
import com.fasterxml.jackson.annotation.JsonProperty; 
import com.fasterxml.jackson.annotation.JsonPropertyOrder; 

import java.time.Instant; 

import static java.util.Objects.requireNonNull; 

@JsonPropertyOrder({"n", "t"}) 
public final class Message { 

    private final String name; 
    private final Instant timestamp; 

    @JsonCreator 
    public Message(@JsonProperty("n" final String name, 
        @JsonProperty("t" final Instant timestamp) { 
     this.name = requireNonNull(name); 
     this.timestamp = requireNonNull(timestamp); 
    } 

    @JsonProperty("n") 
    public String getName() { 
     return this.name; 
    } 

    @JsonProperty("t") 
    public Instant getTimestamp() { 
     return this.timestamp; 
    } 

    // equals(Object), hashCode() and toString() 
} 

的請求做過這樣的:

import javax.ws.rs.client.Entity; 
import javax.ws.rs.client.WebTarget; 
import javax.ws.rs.core.MediaType; 

public final class Gateway { 

    private final WebTarget endpoint; 

    public Message postSomething(final Something something) { 
     return this.endpoint 
       .request(MediaType.APPLICATION_JSON_TYPE) 
       .post(Entity.json(something), Message.class); 
    } 

    // where Message is the class defined above and Something is a similar DTO 
} 

JSON序列化和反序列化工作正常的字符串,整型,BigIntegers,列表等。然而,當我做這樣的事情在我的測試System.out.println(gateway.postSomthing(new Something("x", "y"));我得到以下異常:

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 ('Fri, 22 Sep 2017 10:26:52 GMT') 
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 562] (through reference chain: Message["t"]) 
     at org.example.com.ServiceClientTest.test(ServiceClientTest.java:52) 
Caused by: 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 ('Fri, 22 Sep 2017 10:26:52 GMT') 
at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 1, column: 562] (through reference chain: Message["t"]) 
     at org.example.com.ServiceClientTest.test(ServiceClientTest.java:52) 

從中我得出結論,傑克遜沒有按不知道如何將字符串反序列化爲Instants。我發現了關於這個主題的博客和SO問題,但是我沒有找到關於如何使其工作的明確解釋。

請注意,我希望服務客戶端處理日期字符串,如"Fri, 22 Sep 2017 10:26:52 GMT"以及"2017-09-22T10:26:52.123Z",但我希望它始終序列化爲ISO 8601日期字符串。

誰可以解釋如何使反序列化成Instant工作?

+0

如果JavaTimeModule未在ObjectMapper中註冊,通常會發生這種情況:https://github.com/FasterXML/jackson-modules-java8#user-content-registering-modules –

+0

@Hugo我讀到了這個,但我不清楚如何做這個註冊。你能否將這一切歸結爲答案? – Rinke

+0

我從來沒有同時使用澤西和傑克遜,所以不知道如何去做。我通常以編程方式設置ObjectMapper(如前面評論中提供的鏈接)。無論如何,也許這可以幫助:https://stackoverflow.com/q/18872931/7605325 –

回答

1

在示例代碼中,您當前取決於jersey-media-json-jackson。根據傑克遜的JAX-RS JSON,您可能會更好一些,因爲您可以使用標準JAX-RS API(以及Jackson API的cource)配置傑克遜映射器。

<dependency> 
     <groupId>com.fasterxml.jackson.jaxrs</groupId> 
     <artifactId>jackson-jaxrs-json-provider</artifactId> 
     <version>${jackson.version}</version> 
    </dependency> 

卸下球衣,媒體JSON - 傑克遜並添加傑克遜JAXRS JSON的提供商的依賴後,你可以配置JacksonJaxbJsonProvider和產生客戶端類註冊它:

import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.SerializationFeature; 
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; 

import javax.ws.rs.Produces; 
import javax.ws.rs.client.Client; 
import javax.ws.rs.client.ClientBuilder; 

import static com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS; 

public class ClientProducer { 

    private JacksonJsonProvider jsonProvider; 

    public ClientProducer() { 
     // Create an ObjectMapper to be used for (de)serializing to/from JSON. 
     ObjectMapper objectMapper = new ObjectMapper(); 
     // Register the JavaTimeModule for JSR-310 DateTime (de)serialization 
     objectMapper.registerModule(new JavaTimeModule()); 
     // Configure the object mapper te serialize to timestamp strings. 
     objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); 
     // Create a Jackson Provider 
     this.jsonProvider = new JacksonJaxbJsonProvider(objectMapper, DEFAULT_ANNOTATIONS); 
    } 

    @Produces 
    public Client produceClient() { 

     return ClientBuilder.newClient() 
       // Register the jsonProvider 
       .register(this.jsonProvider); 
    } 
} 

希望這有助於。