java 使用 ObjectMapper + JavaTimeModule 向 Jersey 2 客户端注册 JacksonJsonProvider

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42468133/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 06:40:18  来源:igfitidea点击:

Registering HymansonJsonProvider with ObjectMapper + JavaTimeModule to Jersey 2 Client

javaHymansonjersey-2.0jsr310

提问by libnull-dev

I'm trying to marshal response containing ISO formatted timestamp like that:

我正在尝试封送包含 ISO 格式时间戳的响应,如下所示:

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

into ZonedDateTimefield in my domain model object. Eventually it works if I use solution that is commented in following snippet.There are many similar questions on SO but I would like to get a specific answer what is wrong with another approach which uses HymansonJsonProviderwith ObjectMapper + JavaTimeModule?

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

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        HymansonJsonProvider provider = new HymansonJsonProvider(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(HymansonFeature.class)
                .build();

Error I get:

我得到的错误:

javax.ws.rs.client.ResponseProcessingException: com.fasterxml.Hymanson.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

Project dependencies are:

项目依赖是:

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

edit

编辑

Deserialization happens in here:

反序列化发生在这里:

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

回答by Paul Samsotha

Eventually it works if I use solution that is commented in following snippet.

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

First of all, you are missing a dependency in your list, that you also have, which is the problem.

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

jersey-media-json-Hymanson

jersey-media-json-Hymanson

This module depends on the native Hymanson module that has the HymansonJsonProvider. When you register the HymansonFeature(that comes with jersey-media-json-Hymanson), it registers its own HymansonJaxbJsonProvider, which seems to take precedence over any that you provide.

此模块依赖于具有HymansonJsonProvider. 当您注册HymansonFeature(随jersey-media-json-Hymanson)时,它会注册自己的HymansonJaxbJsonProvider,这似乎优先于您提供的任何内容。

When you use the ContextResolver, the HymansonJsonProvideractually looks-up that ContextResolverand uses it to resolve the ObjectMapper. That's why it works. Whether you used the HymansonFeatureor registered your own HymansonJsonProvider(without configuring an ObjectMapperfor it) the ContextResovlerwould work.

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

Another thing about the jersey-media-json-Hymansonmodule, it that it participates in Jersey's auto-discoverablemechanism, which registers it's HymansonFeature. So even if you didn't explicitly register it, it would still be registered. The only ways to avoid it being registered are to:

关于该jersey-media-json-Hymanson模块的另一件事,它参与 Jersey 的自动发现机制,该机制将其注册为HymansonFeature. 所以即使你没有明确注册它,它仍然会被注册。避免它被注册的唯一方法是:

  1. Disable the auto-discovery (as mention in the previous link)
  2. Don't use the jersey-media-json-Hymanson. Just use the Hymanson native module Hymanson-jaxrs-json-provider. Thing about this though is that, the jersey-media-json-Hymansonadds a couple features on top of the the native module, so you would lose those.
  3. Haven't tested, but it seems that if you use HymansonJaxbJsonProviderinstead of HymansonJsonProvider, it might work. If you look at the source for the HymansonFeature, you will see that it checks for an already registered HymansonJaxbJsonProvider. If there is one, it won't register it's own.

    The one thing I'm not sure about with this is the auto-discoverable. The order in which it is registered, if it will affect whether or not it catches your registered HymansonJaxbJsonProvider. Something you can test out.

  1. 禁用自动发现(如上一个链接中所述
  2. 不要使用jersey-media-json-Hymanson. 只需使用 Hymanson 本机模块Hymanson-jaxrs-json-provider。不过,关于这一点的问题是,它jersey-media-json-Hymanson在本机模块之上添加了一些功能,因此您会丢失这些功能。
  3. 尚未测试,但似乎如果您使用HymansonJaxbJsonProvider而不是HymansonJsonProvider,它可能会起作用。如果您查看 的HymansonFeature,您将看到它检查已注册的HymansonJaxbJsonProvider. 如果有,它不会注册它自己的。

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

回答by Mircea Stanciu

From my pet project:

从我的宠物项目:

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

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

where

在哪里

@Provider
public class HymansonConfig implements ContextResolver<ObjectMapper> {

    private final ObjectMapper objectMapper;

    public HymansonConfig() {
        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;
    }
}

回答by Darshan Mehta

Hymanson configuration looks fine, I tried the following and was able to deserialize the value:

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

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

I can reproduce it by commenting out mapper.registerModule(new JavaTimeModule());. So, it looks like jersey client is not using custom mapperinstance. Could you try configuring it as described here.

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