使用Jackson将Instant序列化为ISO8601格式时强制包含毫秒值

18

我在一个使用 Spring Boot 2.0.0.M6Spring Framework 5.0.1.RELEASEJackson 2.9.2 的项目中有一些与 Jackson 序列化 JSON 相关的问题。

我在 application.properties 中配置了以下与 Jackson 相关的设置:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false

序列化大部分情况下都符合我的要求。但是,我注意到如果毫秒数是000,Jackson似乎会截断它们。

测试1:将毫秒设置为000的序列化时刻:

  • 使用Instant.parse("2017-09-14T04:28:48.000Z")初始化时刻字段
  • 使用Jackson对其进行序列化
  • 输出将为"2017-09-14T04:28:48Z"

测试2:将毫秒设置为某些非000的值的序列化时刻:

  • 使用Instant.parse("2017-09-14T04:28:48.100Z")初始化时刻字段
  • 使用Jackson对其进行序列化
  • 输出将为"2017-09-14T04:28:48.100Z"

问题:

  • 这种行为是有意设计的吗?
  • 我是否可以做任何事来强制序列化为000

看起来类似于这个 github 问题 https://github.com/FasterXML/jackson-datatype-jsr310/issues/39 - Sean Carroll
@SeanCarroll 是的,确实如此。感谢您指出这一点。 - André Gasser
6个回答

12
我会使用以下方法解决问题:

我使用以下方法来解决:

ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(Instant.class, new InstantSerializerWithMilliSecondPrecision());
objectMapper.registerModule(module);

对于带毫秒精度的InstantSerializerWithMilliSecondPrecision,我使用了以下代码:

public class InstantSerializerWithMilliSecondPrecision extends InstantSerializer {

    public InstantSerializerWithMilliSecondPrecision() {
        super(InstantSerializer.INSTANCE, false, new DateTimeFormatterBuilder().appendInstant(3).toFormatter());
    }
}

现在的即时序列化方式总是包含毫秒。例如:2019-09-27T02:59:59.000Z

5
这个问题似乎在这里有一个与Jackson有关的问题该链接包含两种解决方法。 解决方法1:
 ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new JavaTimeModule());
    SimpleModule module = new SimpleModule();
    module.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
        @Override
        public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ").format(zonedDateTime));
        }
    });
    objectMapper.registerModule(module);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

解决方法2

JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(ZonedDateTime.class,
  new ZonedDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")));
ObjectMapper mapper = new ObjectMapper().registerModule(javaTimeModule);

*链接失效,因为他们废弃了FasterXML/jackson-datatype-jsr310并将其移动到jackson-modules-java8。请参见https://github.com/FasterXML/jackson-modules-java8/issues/76


很遗憾,我的瞬时对象在序列化时仍然没有.000。我尝试了你的第一个解决方法。我创建了一个配置类,并使用Primary注释添加了一个ObjectMapper bean,以覆盖自动配置的对象映射器。 - André Gasser
我尝试了Jackson 2.9.5,但这个问题仍未解决。不过你的第一个解决方法有效,谢谢。 - vanillaSugar
我在这里 https://github.com/FasterXML/jackson-modules-java8/issues?q=is%3Aissue+is%3Aclosed 寻找问题...也许这就是那个问题?https://github.com/FasterXML/jackson-modules-java8/issues/70 - vanillaSugar
解决方法1对我有效。而且你应该注意已注册模块的顺序。 - Max Peng
如果毫秒和秒数都是000和00,则问题会进一步延长到秒。如果我使用2020-02-04T08:26.00.000Z,我的输出将是2020-02-04T08:26Z。有什么解决方法吗? - Bhupesh_decoder

2
如果您正在尝试在Spring Boot中实现此操作并想要使用@Gustavo的答案,请按如下方式操作。
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public Module javaTimeModule() {
        JavaTimeModule module = new JavaTimeModule();
        module.addSerializer(new InstantSerializerWithMilliSecondPrecision());
        return module;
    }

}

1

Sean Carroll提到的两种解决方法都不适用于我。最终我只能自己编写Instant序列化器。

final ObjectMapper mapper = new ObjectMapper();
final JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Instant.class, new KeepMillisecondInstantSerializer());
mapper.registerModule(javaTimeModule);

public class KeepMillisecondInstantSerializer extends JsonSerializer<Instant> {

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")
            .withZone(ZoneId.of("UTC"));

    @Override
    public void serialize(final Instant instant, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException {
        final String serializedInstant = dateTimeFormatter.format(instant);
        jsonGenerator.writeString(serializedInstant);
    }
}

我猜Jackson默认使用Instant.toString()方法来序列化Instant对象。我还发现在StackOverflow上有一些关于Instant.toString()方法的讨论。

0
与其修复Jackson库的错误,以下可能是一个快速解决方法: 在您拥有Timestamp变量的POJO类中创建一个字符串变量:
private Timestamp createTimeStamp;

private String stringCreateTimeStamp;   

将时间戳值捕获为字符串:

listOfPojo.forEach(pojo-> {
            pojo.setStringCreateTimeStamp(request.getcreateTimeStamp().toString());
        });

请参考https://www.baeldung.com/java-string-to-timestamp进行转换。

0
使用自定义序列化程序解决 LocalDateTimeZonedDateTime 类的问题。 我的解决方案适用于我在 API 响应中仅使用这两个类来表示日期和时间!我不使用 Instant 或 Date,请注意。
@Configuration
class JacksonConfig {

@Bean
fun objectMapper(): ObjectMapper {
    val mapper = ObjectMapper()
    val javaTimeModule = JavaTimeModule().apply {
        addSerializer(LocalDateTime::class.java, KeepMillisecondLocalDateTimeSerializer())
        addSerializer(ZonedDateTime::class.java, KeepMillisecondZonedDateTimeSerializer())
    }
    mapper.registerModule(javaTimeModule)
    return mapper
}

class KeepMillisecondZonedDateTimeSerializer : JsonSerializer<ZonedDateTime>() {
    private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")

    @Throws(IOException::class)
    override fun serialize(
        value: ZonedDateTime,
        jsonGenerator: JsonGenerator,
        serializerProvider: SerializerProvider?
    ) {
        jsonGenerator.writeString(formatter.format(value))
    }
}

class KeepMillisecondLocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
    private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")

    @Throws(IOException::class)
    override fun serialize(
        value: LocalDateTime,
        jsonGenerator: JsonGenerator,
        serializerProvider: SerializerProvider?
    ) {
        jsonGenerator.writeString(formatter.format(value))
    }
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接