web-dev-qa-db-ja.com

Spring MVCのJersey 2クライアントでJacksonを使用してJoda DateTimeをデシリアライズする方法は?

私はしばらくの間、この概念実証で頭を悩ませてきました。 ISO8601 UTCタイムスタンプでJSONペイロードを返すRESTエンドポイントを消費したい:

{  ...
  "timestamp" : "2014-08-20T11:51:31.233Z" 
}

そして、コマンドラインJava Jackson/Spring Bootを備えたJersey 2クライアントとして記述されたクライアント。マーシャリングPOJOは次のように定義されています。

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
public class GreetingResource
{
  @JsonProperty("timestamp")
  private DateTime date;

  ...
}

以下に示す推奨事項に従うと:

https://jersey.Java.net/documentation/latest/user-guide.html#json.jackson

そして、次のGradle依存関係を使用します:

dependencies {
    compile("org.springframework.boot:spring-boot-starter")
    compile("org.springframework.boot:spring-boot-starter-logging")
    compile("joda-time:joda-time")
    compile("com.fasterxml.jackson.core:jackson-core")
    compile("com.fasterxml.jackson.core:jackson-annotations")
    compile("com.fasterxml.jackson.core:jackson-databind")
    compile("com.fasterxml.jackson.datatype:jackson-datatype-joda")
    compile("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.3.3")
    compile("org.Apache.commons:commons-lang3:3.3.2")
    compile("org.glassfish.jersey.core:jersey-client:2.2")
    compile("org.glassfish.jersey.media:jersey-media-json-jackson:2.2")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

私は常にこのエラーを受け取ります:

Exception in thread "main" javax.ws.rs.ProcessingException: Error reading entity from input stream.
  at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.Java:849)
  at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.Java:768)
  at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.Java:96)
  at org.glassfish.jersey.client.ScopedJaxrsResponse.access$001(ScopedJaxrsResponse.Java:56)
  at org.glassfish.jersey.client.ScopedJaxrsResponse$1.call(ScopedJaxrsResponse.Java:77)
  at org.glassfish.jersey.internal.Errors.process(Errors.Java:315)
  at org.glassfish.jersey.internal.Errors.process(Errors.Java:297)
  at org.glassfish.jersey.internal.Errors.process(Errors.Java:228)
  at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.Java:397)
  at org.glassfish.jersey.client.ScopedJaxrsResponse.readEntity(ScopedJaxrsResponse.Java:74)
  at client.GreetingClient.processResponse(GreetingClient.Java:62)
  at client.GreetingClient.performGet(GreetingClient.Java:53)
  at client.GreetingService.internalLoadGreeting(GreetingService.Java:44)
  at client.GreetingService.LoadGreeting(GreetingService.Java:27)
  at client.Application.main(Application.Java:25)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class org.joda.time.DateTime] from String value ('2014-08-20T12:19:36.358Z'); no single-String constructor/factory method (through reference chain: client.GreetingResource["timestamp"])
  at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator._createFromStringFallbacks(StdValueInstantiator.Java:428)
  at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.Java:299)
  at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.Java:1150)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.Java:139)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.Java:126)
  at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.Java:525)
  at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.Java:99)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.Java:242)
  at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.Java:118)
  at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.Java:1233)
  at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.Java:677)
  at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.Java:777)
  at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.Java:188)
  at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.Java:134)

あきらめました。私は何を間違えていますか?

クライアントは次のように構成されます。

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import org.glassfish.jersey.client.ClientConfig;

...

@Component
public class GreetingClient
{

  private final WebTarget serviceWebTarget;

  {
    ClientConfig config = new ClientConfig();
    config.register(MyObjectMapperProvider.class);
    config.register(JacksonFeatures.class);
    Client client = ClientBuilder.newClient(config);
    this.serviceWebTarget = client.target("http://myserver:8080");
  }

  ...  

}

登録されたプロバイダーは次のように定義されます(クライアントと同じパッケージ内):

@Provider
public class MyObjectMapperProvider implements ContextResolver<ObjectMapper>
{
  private final static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");


  @Override
  public ObjectMapper getContext(Class<?> type)
  {
    final ObjectMapper result = new JodaMapper();
    result.setDateFormat(dateFormat);
    return result;
  }

}

プロバイダーを登録するかどうかに関係なく、DateTimeDeserializerを使用するようにフィールドに注釈を付けました(@JsonDeserializeを介して)。

代わりに標準のJava.util.Dateを使用する場合

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")

それは風のように機能します。

手がかりはありますか?ご協力いただきありがとうございます。

19
Reynaldo

以下のテストは正常に機能します。

import Java.io.IOException;

import org.joda.time.DateTime;
import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.joda.JodaModule;

public class JacksonTest {

    private static final String json = "{ \n" +
            "  \"timestamp\" : \"2014-08-20T11:51:31.233Z\" \n" +
            "}";

    @Test
    public void test() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JodaModule());

        System.out.println(mapper.readValue(json, GreetingResource.class));
    }
}

@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(Include.NON_NULL)
class GreetingResource {
    @JsonProperty("timestamp")
    private DateTime date;

    public DateTime getDate() {
        return date;
    }

    public void setDate(DateTime date) {
        this.date = date;
    }

    @Override
    public String toString() {
        return "GreetingResource{" +
                "date=" + date +
                '}';
    }
}

Mavenの構成:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.4.1.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.4.1.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.4.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-joda</artifactId>
    <version>2.4.0</version>
</dependency>
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.4</version>
</dependency>
12
Michał Ziober

問題がある他の人にとって、他の答えはどれも完全に完全ではなかったので、これは

WebTarget target = ClientBuilder.newClient().target(Host);

JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();

provider.setMapper(new JodaMapper());

target.register(provider);
6
devshorts

私は同じ問題に遭遇しました。必要なのは、カスタムObjectMapperをジャージーに登録することです。

@Component
public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
        provider.setMapper(new JodaMapper());

        register(provider);
        packages("endpoints.package.names");
    }
}
4
Piotr Glazar

Piotor Glazarは一方には機能しますが、もう一方には機能しません。 (日付はISO8601 UTCではなく、タイムスタンプとして書き込まれます。)

彼のソリューションからの1つの変更は、SerializationFeature.WRITE_DATES_AS_TIMESTAMPSを無効にすることです。

ここに私のために働いた解決策があります。

public class JerseyConfig extends ResourceConfig {

    public JerseyConfig() {
        JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
        JodaMapper jodaMapper = new JodaMapper();
        jodaMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        provider.setMapper(jodaMapper);

        register(provider);
    }
}
1
Bojan Petkovic

JersyクライアントのJ2EEプロジェクトで同じことを達成したい場合は、以下のコードを参照してください。

    final Client client = ClientBuilder.newClient();
    final WebTarget target = client.target(URI);

    final JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
    final JodaMapper jodaMapper = new JodaMapper();
    jodaMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

    //while sending date to server.
    //Use a consistent format to send to server
    final DateFormat format = new SimpleDateFormat(DATE_TIME_FORMAT);
    jodaMapper.setDateFormat(format);

    //receiving date from server. [Deserialize]
    //expect any ISO format or any other format and make sure we handle it.
    final SimpleModule module = new SimpleModule();
    module.addDeserializer(Date.class, new CustumDateDeserializer());
    jodaMapper.registerModule(module);

    provider.setMapper(jodaMapper);

    target.register(provider);

そして、日付DeSerialiserは以下です、

private class CustumDateDeserializer extends JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {

        final String stringDate = jp.getText();
        Date parsedDate = null;

        if(stringDate == null || stringDate.trim().isEmpty()) {
            return parsedDate;
        }

        try{
            final DateTimeFormatter parser = ISODateTimeFormat.dateTime();
            parsedDate = parser.parseDateTime(stringDate).toDate();
        }catch(IllegalArgumentException e) {
            //use the default parser if the given date is not iso-format.
            parsedDate = new DateDeserializers.DateDeserializer().deserialize(jp, ctxt);
        }

        return parsedDate;
    }
}

これがお役に立てば幸いです。

0
Lyju I Edwinson