web-dev-qa-db-ja.com

ObjectMapper + JavaTimeModuleを使用したJacksonJsonProviderのJersey 2クライアントへの登録

私はISO形式のタイムスタンプを含む応答をそのようにマーシャリングしようとしています:

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

ドメインモデルオブジェクトのZonedDateTimeフィールドに入力します。最終的には、次のスニペットでコメントされているソリューションを使用すれば機能します。SOにも同様の質問がたくさんありますが、JacksonJsonProvider with 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>>(){});
10
libnull-dev

最終的には、次のスニペットでコメントされているソリューションを使用すれば機能します。

まず、リストに依存関係がありません。これも問題です。

jersey-media-json-jackson

このモジュールは、JacksonJsonProviderを持つネイティブJacksonモジュールに依存しています。 JacksonFeaturejersey-media-json-jacksonに付属)を登録すると、独自のJacksonJaxbJsonProviderが登録されます。これは、指定したものよりも優先されるようです。

ContextResolverを使用すると、JacksonJsonProviderは実際にそのContextResolverを検索し、それを使用してObjectMapperを解決します。それが機能する理由です。 JacksonFeatureを使用した場合でも、独自のJacksonJsonProviderを登録した場合でも(ObjectMapperを構成せずに)ContextResovlerが機能します。

jersey-media-json-jacksonモジュールについてもう1つ、ジャージの auto-discoverable メカニズムに参加し、JacksonFeatureを登録します。したがって、明示的に登録しなかった場合でも、登録されます。登録されないようにする唯一の方法は次のとおりです。

  1. 自動検出を無効にする( 前のリンク で言及されているように)
  2. jersey-media-json-jacksonは使用しないでください。 Jacksonネイティブモジュールjackson-jaxrs-json-providerを使用するだけです。ただし、jersey-media-json-jacksonを使用すると、ネイティブモジュールの上にいくつかの機能が追加されるため、これらの機能は失われます。
  3. テストはしていませんが、JacksonJaxbJsonProviderの代わりにJacksonJsonProviderを使用すると、動作する可能性があるようです。 JacksonFeatureのソース を見ると、すでに登録されているJacksonJaxbJsonProviderをチェックしていることがわかります。存在する場合、それ自体は登録されません。

    これについて私が確信していないことの1つは、自動検出可能です。登録されたJacksonJaxbJsonProviderをキャッチするかどうかに影響する場合は、登録された順序。テストできる何か。

11
Paul Samsotha

私のペットプロジェクトから:

<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;
    }
}
3
Mircea Stanciu

ジャクソンの設定は問題ないようです。私は次のことを試して、値を逆シリアル化することができました。

_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インスタンスを使用していないようです。説明されているように設定してみてください ここ

1
Darshan Mehta