使用 Spring RestControllers 和 Jackson JSON 解析后端,前端使用 AngularJS。我正在寻找一种有效的方法,让 Jackson 将 Instant 序列化为纪元毫秒,以便随后方便地使用 JavaScript 代码。 (在浏览器端,我希望通过 Angular 的日期过滤器提供纪元 ms:
{{myInstantVal | date:'short' }}
以获得我想要的日期格式。)
在 Java 方面,Jackson 使用的 getter 很简单:
public Instant getMyInstantVal() { return myInstantVal; }
序列化无法按原样工作,因为 jackson-datatype-jsr310 默认情况下不会返回 Instant 的 Epoch 毫秒。我考虑将 @JsonFormat 添加到上面的 getter 中,将 Instant 转变为前端可以使用的东西,但它遇到两个问题:(1)我可以提供的模式显然仅限于 SimpleDateFormat,它不提供“纪元毫秒”选项,并且(2)当我尝试将 Instant 作为格式化日期发送到浏览器时,Jackson 抛出异常,因为 @JsonFormat 注释需要 Instant 的 TimeZone 属性,这是我不希望的硬编码,因为它会因用户而异。
到目前为止,我的解决方案(并且工作正常)是使用 @JsonGetter 创建替换 getter,这会导致 Jackson 使用此方法来序列化
myInstantVal
:
@JsonGetter("myInstantVal")
public long getMyInstantValEpoch() {
return myInstantVal.toEpochMilli();
}
这是这样做的正确方法吗?或者我是否缺少一个很好的注释,可以将其放在 getMyInstantVal() 上,这样我就不必创建这些附加方法?
您只需阅读链接到的自述文件即可。强调我的:
如果启用了 SerializationFeature#WRITE_DATES_AS_TIMESTAMPS 功能,大多数 JSR-310 类型都会序列化为数字(适当的整数或小数),否则会以标准 ISO-8601 字符串表示形式序列化。
[...]时间戳的粒度是通过配套功能 SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS 和 DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS 控制的。对于序列化,如果启用了 WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(默认情况下),时间戳会写为小数(小数),其中数字为秒,小数为小数秒,分辨率可达纳秒,具体取决于底层 JDK 实现。
如果禁用 WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS,时间戳将以毫秒整数形式写入。
data class MyPojo(
@JsonFormat(without = [JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS])
val timestamp: Instant
)
@JsonFormat(shape = JsonFormat.Shape.NUMBER, without = JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
public Instant getMyInstantVal() { return myInstantVal; }
<mvc:annotation-driven validator="beanValidator">
<mvc:message-converters register-defaults="true">
<beans:ref bean="jsonConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
true,很可能您希望保持 Spring 按原样配置其他转换器)。
<beans:bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<beans:property name="objectMapper">
<beans:bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<beans:property name="featuresToDisable">
<beans:array>
<util:constant static-field="com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS"/>
</beans:array>
</beans:property>
</beans:bean>
</beans:property>
部分 H/T
这个 SO 帖子。
@JsonFormat(shape = JsonFormat.Shape.NUMBER, without = JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS)
public Instant getMyInstantVal() { return myInstantVal; }
为了反序列化 millis,您可以使用自定义配置:
@Configuration
@RequiredArgsConstructor
public class JsonDateTimeSerdeConfig {
private final RequestMappingHandlerAdapter handlerAdapter;
@PostConstruct
public void handleContextRefresh() {
handlerAdapter
.getMessageConverters()
.forEach(c -> {
if (c instanceof MappingJackson2HttpMessageConverter) {
MappingJackson2HttpMessageConverter jsonMessageConverter = (MappingJackson2HttpMessageConverter) c;
ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
objectMapper.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS);
}
});
}
它肯定会影响服务中的所有时间戳,但对我来说,在同一个服务中使用不同的 serde 策略会很奇怪。
@JsonFormat(shape = JsonFormat.Shape.NUMBER, timezone = "UTC")
private Instant createdAt;
这将导致以下响应:
{
...
"createdAt": 1534923249,
...
}