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

问题描述 投票:0回答:6

使用 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() 上,这样我就不必创建这些附加方法?

json spring jackson java-time
6个回答
39
投票

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

如果启用了 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,时间戳将以毫秒整数形式写入。


6
投票
这就是 Kotlin 中对我有用的东西(对于 Java 来说应该是一样的)。这使您可以序列化为纪元毫秒,而无需更改 ObjectMapper 的配置

data class MyPojo( @JsonFormat(without = [JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS]) val timestamp: Instant )
    

2
投票
要申请单个房产,请使用此注释:

@JsonFormat(shape = JsonFormat.Shape.NUMBER, without = JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) public Instant getMyInstantVal() { return myInstantVal; }
    

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

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

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

(上面的注册默认值默认情况下是

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

这个 SO 帖子。


0
投票
    JsonFormat注释: 上面关于 @JsonFormat 注释的观点是有效的。但如果您需要处理序列化和反序列化,您可能需要确保配置适应这两个方面。提供的代码片段解决了序列化部分,但您可能还需要配置反序列化。
@JsonFormat(shape = JsonFormat.Shape.NUMBER, without = JsonFormat.Feature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS) public Instant getMyInstantVal() { return myInstantVal; }

    建议的 Spring Boot 自动配置不起作用:
  1. https://github.com/FasterXML/jackson-modules-java8/issues/1
为了反序列化 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 策略会很奇怪。


-2
投票
在 Instant 属性的 JSON 响应中返回纪元毫秒的简单方法如下:

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

这将导致以下响应:

{ ... "createdAt": 1534923249, ... }
    
© www.soinside.com 2019 - 2024. All rights reserved.