Wie Joda DateTime mit Jackson mit Jersey 2 Client im Frühjahr MVC deserialisieren?

Ich habe meinen Kopf für eine Weile mit diesem Proof of Concept geschlagen. Ich möchte einen REST-Endpunkt verwenden, der JSON-Nutzdaten mit einem ISO8601-UTC-Zeitstempel zurückgibt:

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

und ich möchte es unter Verwendung eines Java-Befehlszeilenclients verwenden, der als Jersey 2-Client mit Jackson / Spring Boot geschrieben wurde. Das Rangier-POJO ist wie folgt definiert:

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

  ...
}

Nach folgenden Empfehlungen in:

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

und unter Verwendung der folgenden Gradle-Abhängigkeiten:

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")
}

Ich bekomme immer diesen Fehler:

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)

Ich habe aufgegeben. Was mache ich falsch?

Der Client ist wie folgt konfiguriert:

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

  ...  

}

Der registrierte Anbieter ist definiert als (im selben Paket wie der Kunde):

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

}

Ich habe versucht, mit / ohne Registrierung des Anbieters und auch Annotieren des Felds, um DateTimeDeserializer (über @JsonDeserialize) zu verwenden - nur um einen Fehler zu erhalten, weil "kein Standardkonstruktor ohne Argument verfügbar ist".

Wenn ich stattdessen standard java.util.Date mit benutze

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

es funktioniert wie eine Brise.

Irgendwelche Hinweise? Danke für Ihre Hilfe.

Antworten auf die Frage(5)

Ihre Antwort auf die Frage