让 Jackson 将 Java 8 Instant 序列化为纪元毫秒的有效方法?

Efficient way to have Jackson serialize Java 8 Instant as epoch milliseconds?

使用 Spring RestControllers 和 Jackson JSON 解析后端,在前端使用 AngularJS。我正在寻找一种有效的方法让 Jackson 将 Instant 序列化为纪元毫秒,以便随后方便地使用 JavaScript 代码。 (在浏览器端,我希望通过 Angular 的 Date Filter: {{myInstantVal | date:'short' }} 为我想要的日期格式提供纪元 ms。)

在 Java 方面,Jackson 使用的 getter 很简单:

public Instant getMyInstantVal() { return myInstantVal; }

序列化不会按原样工作,因为上面 getter 的 jackson-datatype-jsr310 doesn't return Epoch milliseconds by default for an Instant. I looked at adding @JsonFormat 将 Instant 变成前端可以使用的东西,但它有两个问题:( 1) 我可以提供的模式显然仅限于不提供 "epoch milliseconds" 选项的 SimpleDateFormat,并且 (2) 当我尝试将 Instant 作为格式化日期发送到浏览器时,Jackson 抛出异常因为 @JsonFormat 注释需要 Instants 的 TimeZone 属性,我不希望硬编码,因为它会因用户而异。

我目前的解决方案(并且工作正常)是使用 @JsonGetter 创建替换 getter,这导致 Jackson 使用此方法而不是序列化 myInstantVal:

@JsonGetter("myInstantVal")
public long getMyInstantValEpoch() {
    return myInstantVal.toEpochMilli();
}

这是正确的做法吗?或者是否有一个我遗漏的很好的注释,我可以放在 getMyInstantVal() 上,这样我就不必创建这些额外的方法了?

您只需要阅读您链接到的自述文件。强调我的:

Most JSR-310 types are serialized as numbers (integers or decimals as appropriate) if the SerializationFeature#WRITE_DATES_AS_TIMESTAMPS feature is enabled, and otherwise are serialized in standard ISO-8601 string representation.

[...]

Granularity of timestamps is controlled through the companion features SerializationFeature#WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS and DeserializationFeature#READ_DATE_TIMESTAMPS_AS_NANOSECONDS. For serialization, timestamps are written as fractional numbers (decimals), where the number is seconds and the decimal is fractional seconds, if WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS is enabled (it is by default), with resolution as fine as nanoseconds depending on the underlying JDK implementation. If WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS is disabled, timestamps are written as a whole number of milliseconds.

添加到 JB 的答案中,覆盖 Spring MVC 的默认 JSON 解析器以从 Instant(以及其他 Java 8 个具有它们的日期对象)中去除纳秒:

  1. 在 mvc:annotation 驱动元素中,指定您将覆盖默认的 JSON 消息转换器:

    <mvc:annotation-driven validator="beanValidator"> <mvc:message-converters register-defaults="true"> <beans:ref bean="jsonConverter"/> </mvc:message-converters> </mvc:annotation-driven>

(上面的 register-defaults 默认是 true 并且很可能您希望保持由 Spring 按原样配置的其他转换器)。

  1. 重写 MappingJackson2HttpMessageConverter 如下:

    <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>
    

步骤 #1 很重要,因为 Spring MVC 将忽略配置的 MJ2HMC 对象,转而使用它自己的默认对象。

部分 H/T this 所以 post.

在 JSON 即时 属性 响应中 return 纪元的简单方法如下:

@JsonFormat(shape = JsonFormat.Shape.NUMBER, timezone = "UTC")
private Instant createdAt;

这将导致以下响应:

{
  ...
  "createdAt": 1534923249,
  ...
}