15

我正在尝试编组包含这样的 ISO 格式时间戳的响应:

{
...
    "time" : "2014-07-02T04:00:00.000000Z"
...
}

进入ZonedDateTime我的域模型对象中的字段。如果我使用以下代码段中注释的解决方案,最终它会起作用。关于 SO 有很多类似的问题,但我想得到一个具体的答案,另一种使用JacksonJsonProviderwith的方法有什么问题ObjectMapper + JavaTimeModule

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        JacksonJsonProvider provider = new JacksonJsonProvider(mapper);
        Client client = ClientBuilder.newBuilder()
//                .register(new ObjectMapperContextResolver(){
//                    @Override
//                    public ObjectMapper getContext(Class<?> type) {
//                        ObjectMapper mapper = new ObjectMapper();
//                        mapper.registerModule(new JavaTimeModule());
//                        return mapper;
//                    }
//                })
                .register(provider)
                .register(JacksonFeature.class)
                .build();

我得到的错误:

javax.ws.rs.client.ResponseProcessingException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.ZonedDateTime: no String-argument constructor/factory method to deserialize from String value ('2017-02-24T20:46:05.000000Z')
 at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream@53941c2f

项目依赖项是:

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.8.7'
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-core:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-databind:2.8.7'
compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.7'
compile 'org.glassfish.jersey.core:jersey-client:2.25.1'

编辑

反序列化发生在这里:

CandlesResponse<BidAskCandle> candlesResponse = webTarget.request()
                .header(HttpHeaders.AUTHORIZATION,"Bearer "+token)
                .accept(MediaType.APPLICATION_JSON)
                .get(new GenericType<CandlesResponse<BidAskCandle>>(){});
4

3 回答 3

13

如果我使用以下代码段中注释的解决方案,最终它会起作用。

首先,您的列表中缺少一个依赖项,您也有这个依赖项,这就是问题所在。

jersey-media-json-jackson

该模块依赖于具有JacksonJsonProvider. 当您注册JacksonFeature(附带的jersey-media-json-jackson)时,它会注册自己的JacksonJaxbJsonProvider,这似乎优先于您提供的任何内容。

当您使用 时ContextResolverJacksonJsonProvider实际上会查找ContextResolver并使用它来解决ObjectMapper. 这就是它起作用的原因。无论您是使用JacksonFeature还是注册自己的JacksonJsonProvider(不ObjectMapper为其配置)ContextResovler都可以。

该模块的另一件事是jersey-media-json-jackson,它参与了 Jersey 的自动发现机制,该机制将其注册为JacksonFeature. 因此,即使您没有明确注册它,它仍然会被注册。避免它被注册的唯一方法是:

  1. 禁用自动发现(如上一个链接中所述)1

  2. 不要使用jersey-media-json-jackson. 只需使用杰克逊本机模块jackson-jaxrs-json-provider。不过,关于这一点的事情是,它jersey-media-json-jackson在本机模块之上添加了几个特性,所以你会失去这些。

  3. 尚未测试,但似乎如果您使用JacksonJaxbJsonProvider而不是JacksonJsonProvider,它可能会起作用。如果您查看 的源代码JacksonFeature,您会看到它会检查已注册的JacksonJaxbJsonProvider. 如果有,它不会注册它自己的。

    我不确定的一件事是自动发现。它注册的顺序,如果它会影响它是否抓住你的注册JacksonJaxbJsonProvider。你可以测试的东西。


脚注

1.参见

于 2017-02-27T00:07:20.170 回答
8

从我的宠物项目:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>${jackson.version}</version>
</dependency>

public WebTarget getTarget(URI uri) {
    Client client = ClientBuilder
            .newClient()
            .register(JacksonConfig.class);
    return client.target(uri);
}

在哪里

@Provider
public class JacksonConfig implements ContextResolver<ObjectMapper> {

    private final ObjectMapper objectMapper;

    public JacksonConfig() {
        objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    @Override
    public ObjectMapper getContext(Class<?> aClass) {
        return objectMapper;
    }
}
于 2018-08-15T04:23:51.143 回答
2

杰克逊配置看起来不错,我尝试了以下并能够反序列化该值:

public class Test {

    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        Model model = mapper.readValue("{\"time\" : \"2014-07-02T04:00:00.000000Z\"}", Model.class);
        System.out.println(model.getTime());
    }

}

class Model{
    private ZonedDateTime time;

    public ZonedDateTime getTime() {
        return time;
    }
    public void setTime(ZonedDateTime time) {
        this.time = time;
    }
}

我可以通过注释来重现它mapper.registerModule(new JavaTimeModule());。所以,看起来泽西客户端没有使用自定义mapper实例。您可以尝试按照此处所述进行配置。

于 2017-02-26T12:07:01.107 回答