如何在 Spring MVC 中使用 Jackson 和 Jersey 2 Client 反序列化 Joda DateTime?
Posted
技术标签:
【中文标题】如何在 Spring MVC 中使用 Jackson 和 Jersey 2 Client 反序列化 Joda DateTime?【英文标题】:How to deserialize Joda DateTime using Jackson with Jersey 2 Client in Spring MVC? 【发布时间】:2014-10-13 19:26:33 【问题描述】:一段时间以来,我一直在用这个概念证明来抨击自己。我想使用一个 REST 端点,它返回带有 ISO8601 UTC 时间戳的 JSON 有效负载:
...
"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'")
它就像微风一样工作。
有什么线索吗?感谢您的帮助。
【问题讨论】:
您尝试过使用 Joda Jackson 模块吗? github.com/FasterXML/jackson-datatype-joda 是的,从我的代码中我链接到 com.fasterxml.jackson.datatype:jackson-datatype-joda 【参考方案1】:以下测试正常:
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>
【讨论】:
+1,但@JsonProperty("timestamp")
令人困惑。我认为这是 JodaModule 正确处理转换所必需的,但它只是将生成的反序列化 json 属性名称从“日期”(类字段的名称)更改为“时间戳”。
是的,它就像你写的那样工作。 '@JsonProperty("timestamp")' 只是定义了这个属性的 JSON 名称。
只需添加 mapper.registerModule(new JodaModule());在映射器中添加 jodaTime 依赖项对我有用 :) 谢谢,我被卡住了很久。
在找到您的“mapper.registerModule(new JodaModule());”之前的几个月的挫败感诡计,谢谢!
@Shikha mapper.registerModule(new JodaModule())
工作!谢谢。【参考方案2】:
对于其他有问题的人,其他答案都没有真正完整,所以它是这个
WebTarget target = ClientBuilder.newClient().target(host);
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(new JodaMapper());
target.register(provider);
【讨论】:
【参考方案3】:我遇到了同样的问题。您要做的就是在 Jersey 中注册自定义 ObjectMapper:
@Component
public class JerseyConfig extends ResourceConfig
public JerseyConfig()
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(new JodaMapper());
register(provider);
packages("endpoints.package.names");
【讨论】:
【参考方案4】:Piotor Glazer 以一种方式工作,但不是另一种。 (它将日期写为时间戳,而不是 ISO8601 UTC。)
他的解决方案的一个变化是禁用 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);
【讨论】:
【参考方案5】:如果您想在 J2EE 项目中为 Jersy 客户端实现相同的功能,请参考下面的代码。
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);
Date DeSerializer 在下面,
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;
希望这会对你有所帮助。
【讨论】:
以上是关于如何在 Spring MVC 中使用 Jackson 和 Jersey 2 Client 反序列化 Joda DateTime?的主要内容,如果未能解决你的问题,请参考以下文章
如何在带有注解配置的spring mvc中使用spring数据
我们如何在 Spring MVC 项目中使用 Spring Cloud Sleuth?