我如何(取消)编组日期作为杰克逊的时间戳
How do I (un)marshall a Date as a time-stamp with jackson
我在将 java.util.Date 对象编组(取消)为时间戳时遇到问题。理想情况下,时间戳应采用 UTC-0 格式,而不是服务器的本地时区。尽管如果需要,我可以很容易地解决这个问题。
注意:我知道这里有几个关于堆栈溢出的类似主题,但我遇到的每个人要么已经过时(关于正在使用的 API),要么与序列化 Date 对象有关到字符串。
这是我的 POM 文件的摘录:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.15</version>
</dependency>
样本模型class:
public class PersonJSON {
private Date birthDate;
}
预期输出(假设出生日期为 2015 年 2 月 1 日 00:00:00 UTC-0):
{"birthDate":1422748800000}
当前输出:
{"birthDate":"2015-02-01"}
是否可以解决此问题(最好使用注释)?
解决方案
事实证明错误是由从 Java.sql.Date 到 Java.util.Date 的隐式转换引起的。转换没有抛出异常,并且 sql.Date 扩展 util.Date 所以逻辑上它应该工作,但它没有。解决方案是从 Java.sql.Date 中提取时间戳并将其用作 Java.util.Date 构造函数的输入。
如果你确实想改变格式,假设你有一个有效的 Java.util.Date 对象,peeskillet 列出的方法是正确的。
您只需在 ObjectMapper
上配置 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
。根据我的测试,我不需要配置反序列化,它可以很好地传递时间戳。
@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
}
@Override
public ObjectMapper getContext(Class<?> type) { return mapper; }
}
至于通过每个字段的注释设置此功能,我不确定如何在不编写自定义序列化程序的情况下使用 Jackson 做到这一点(但我不怀疑是否有其他方法)。您始终可以使用 Jackson 提供程序附带的 JAXB 注释支持。随便写一个XmlAdapter
and annotate the field with @XmlJavaTypeAdatper(YourDateAdapter.class)
. Here's
更新
完整示例。
必需的依赖项
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.15</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.15</version>
</dependency>
测试(您需要从上面注册上下文解析器)
import java.util.Date;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import jersey.Whosebug.provider.ObjectMapperContextResolver;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
public class ObjectMapperTest extends JerseyTest {
public static class Person {
public Date birthDate;
}
@Path("/person") @Produces(MediaType.APPLICATION_JSON)
public static class PersonResource {
@GET
public Response getPerson() {
Person person = new Person();
person.birthDate = new Date();
return Response.ok(person).build();
}
}
@Override
public Application configure() {
return new ResourceConfig(PersonResource.class,
ObjectMapperContextResolver.class);
}
@Test
public void test() {
String personJson = target("person").request().get(String.class);
System.out.println(personJson);
}
}
结果:{"birthDate":1423738762437}
更新 2
我认为这个答案不正确。默认情况下,杰克逊似乎已经序列化为时间戳。如果我们不想要时间戳,那么我们将上面的配置设置为false
。但这仍然没有回答 OP 的问题。不确定是什么问题。
我也运行关注这个问题。在我的例子中,Hibernate ORM 将 java.util.Date
实体属性加载为 java.sql.Date
对象,这些对象没有像我预期的那样序列化为时间戳(这是 jackson 在序列化 java.util.Date
对象时采用的默认序列化策略).
我的解决方法是明确指示 java.sql.Date
对象使用杰克逊的库存 com.fasterxml.jackson.databind.ser.std.DateSerializer
class 就像它用于 java.util.Date
对象一样:
private Client buildClient() {
return ClientBuilder.newBuilder()
.register(getJacksonJsonProvider())
.build();
}
private JacksonJsonProvider getJacksonJsonProvider() {
JacksonJsonProvider jjp = new JacksonJaxbJsonProvider();
jjp.setMapper(getJsonObjectMapper());
return jjp;
}
private ObjectMapper getJsonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule module = new SimpleModule();
module.addSerializer(java.sql.Date.class, new DateSerializer()); // <-- My Fix
mapper.registerModule(module);
return mapper;
}
我在将 java.util.Date 对象编组(取消)为时间戳时遇到问题。理想情况下,时间戳应采用 UTC-0 格式,而不是服务器的本地时区。尽管如果需要,我可以很容易地解决这个问题。
注意:我知道这里有几个关于堆栈溢出的类似主题,但我遇到的每个人要么已经过时(关于正在使用的 API),要么与序列化 Date 对象有关到字符串。
这是我的 POM 文件的摘录:
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.15</version>
</dependency>
样本模型class:
public class PersonJSON {
private Date birthDate;
}
预期输出(假设出生日期为 2015 年 2 月 1 日 00:00:00 UTC-0):
{"birthDate":1422748800000}
当前输出:
{"birthDate":"2015-02-01"}
是否可以解决此问题(最好使用注释)?
解决方案
事实证明错误是由从 Java.sql.Date 到 Java.util.Date 的隐式转换引起的。转换没有抛出异常,并且 sql.Date 扩展 util.Date 所以逻辑上它应该工作,但它没有。解决方案是从 Java.sql.Date 中提取时间戳并将其用作 Java.util.Date 构造函数的输入。
如果你确实想改变格式,假设你有一个有效的 Java.util.Date 对象,peeskillet 列出的方法是正确的。
您只需在 ObjectMapper
上配置 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
。根据我的测试,我不需要配置反序列化,它可以很好地传递时间戳。
@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
}
@Override
public ObjectMapper getContext(Class<?> type) { return mapper; }
}
至于通过每个字段的注释设置此功能,我不确定如何在不编写自定义序列化程序的情况下使用 Jackson 做到这一点(但我不怀疑是否有其他方法)。您始终可以使用 Jackson 提供程序附带的 JAXB 注释支持。随便写一个XmlAdapter
and annotate the field with @XmlJavaTypeAdatper(YourDateAdapter.class)
. Here's
更新
完整示例。
必需的依赖项
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.15</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.15</version>
</dependency>
测试(您需要从上面注册上下文解析器)
import java.util.Date;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import jersey.Whosebug.provider.ObjectMapperContextResolver;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
public class ObjectMapperTest extends JerseyTest {
public static class Person {
public Date birthDate;
}
@Path("/person") @Produces(MediaType.APPLICATION_JSON)
public static class PersonResource {
@GET
public Response getPerson() {
Person person = new Person();
person.birthDate = new Date();
return Response.ok(person).build();
}
}
@Override
public Application configure() {
return new ResourceConfig(PersonResource.class,
ObjectMapperContextResolver.class);
}
@Test
public void test() {
String personJson = target("person").request().get(String.class);
System.out.println(personJson);
}
}
结果:{"birthDate":1423738762437}
更新 2
我认为这个答案不正确。默认情况下,杰克逊似乎已经序列化为时间戳。如果我们不想要时间戳,那么我们将上面的配置设置为false
。但这仍然没有回答 OP 的问题。不确定是什么问题。
我也运行关注这个问题。在我的例子中,Hibernate ORM 将 java.util.Date
实体属性加载为 java.sql.Date
对象,这些对象没有像我预期的那样序列化为时间戳(这是 jackson 在序列化 java.util.Date
对象时采用的默认序列化策略).
我的解决方法是明确指示 java.sql.Date
对象使用杰克逊的库存 com.fasterxml.jackson.databind.ser.std.DateSerializer
class 就像它用于 java.util.Date
对象一样:
private Client buildClient() {
return ClientBuilder.newBuilder()
.register(getJacksonJsonProvider())
.build();
}
private JacksonJsonProvider getJacksonJsonProvider() {
JacksonJsonProvider jjp = new JacksonJaxbJsonProvider();
jjp.setMapper(getJsonObjectMapper());
return jjp;
}
private ObjectMapper getJsonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule module = new SimpleModule();
module.addSerializer(java.sql.Date.class, new DateSerializer()); // <-- My Fix
mapper.registerModule(module);
return mapper;
}