简体   繁体   English

Jackson,java.time,ISO 8601,序列化无毫秒

[英]Jackson , java.time , ISO 8601 , serialize without milliseconds

I'm using Jackson 2.8 and need to communicate with an API that doesn't allow milliseconds within ISO 8601 timestamps. 我正在使用Jackson 2.8并且需要与ISO 8601时间戳内不允许毫秒的API进行通信。

The expected format is this: "2016-12-24T00:00:00Z" 预期格式为: "2016-12-24T00:00:00Z"

I'm using Jackson's JavaTimeModule with WRITE_DATES_AS_TIMESTAMPS set to false . 我正在使用Jackson的JavaTimeModule, WRITE_DATES_AS_TIMESTAMPS设置为false

But this will print milliseconds. 但这将打印毫秒。

So I tried to use objectMapper.setDateFormat which didn't change anything. 所以我尝试使用没有改变任何东西的objectMapper.setDateFormat

My current workaround is this: 我目前的解决方法是:

ObjectMapper om = new ObjectMapper();

DateTimeFormatter dtf = new DateTimeFormatterBuilder()
    .appendInstant(0)
    .toFormatter();

JavaTimeModule jtm = new JavaTimeModule();
jtm.addSerializer(Instant.class, new JsonSerializer<Instant>() {
    @Override
    public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        gen.writeString(dtf.format(value));
    }
});

om.registerModule(jtm);

I'm overriding the default serializer for Instant.class which works. 我正在覆盖Instant.class的默认序列化Instant.class


Is there any nice way using some configuration parameter to solve this? 有没有什么好办法使用一些配置参数来解决这个问题?

Update: 更新:

Just add a @JsonFormat annotation whith the date format above the Instant property. 只需在Instant属性上方添加日期格式的@JsonFormat注释即可。 It's very easy. 这很容易。

In the case you have an ObjectMapper with the JavaTimeModule like next: 如果你有一个带有JavaTimeModule的ObjectMapper,就像下一个:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

If you have a class with an Instant property, you should add the @JsonFormat annotation and put the date pattern which hasn't milliseconds. 如果你有一个具有Instant属性的类,你应该添加@JsonFormat注释并放置没有毫秒的日期模式。 It would be like next: 这就像下一个:

public static class TestDate {

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss", timezone = "UTC")
    Instant instant;

    //getters & setters
}

So if you serialize an object to Json it works perfectly: 因此,如果您将对象序列化为Json,它可以完美地运行:

String json = mapper.writeValueAsString(testDate);
System.out.println(json); 

Output 产量

{"instant":"2016-11-10 06:03:06"} {“instant”:“2016-11-10 06:03:06”}


Old Answer. 老答案。 I don't know why but It doesn't work propertly: 我不知道为什么,但它不起作用:

You could use the Jackson2ObjectMapperBuilder to build it. 您可以使用Jackson2ObjectMapperBuilder来构建它。

You just need to add the dateFormat you want. 您只需要添加所需的dateFormat。 It would be something like next: 这将是下一个:

DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

ObjectMapper mapper = Jackson2ObjectMapperBuilder
            .json()       
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) 
            .modules(new JavaTimeModule())
            .dateFormat(dateFormat)
            .build();

Here is an alternative which is something you can set globally, but will need you to use ZonedDateTime with instant formatter as we can't set the format for the Instant Serializer provided with Java Time Module. 这是一个可以全局设置的替代方法,但是需要您使用ZonedDateTime和即时格式化程序,因为我们无法设置随Java Time Module提供的Instant Serializer的格式。

You wont see any side effects of using zoned date time for instant as jackson serializes the zone id separately and is disabled by default. 您不会看到使用分区日期时间为即时的任何副作用,因为杰克逊分别序列化区域ID并默认禁用。 So technically, this is similar to applying the formatter to Instant . 从技术上讲,这类似于将格式化程序应用于Instant

When used this way, the ZonedDateTime serializer delegates the serialization to InstantBaseSerializer and uses the specified custom format. 以这种方式使用时, ZonedDateTime序列化程序将序列化委派给InstantBaseSerializer并使用指定的自定义格式。

@RunWith(JUnit4.class)
public class InstantNoMillisTest {

    private ObjectMapper objectMapper;

    @Before
    public void init() {
        JavaTimeModule module = new JavaTimeModule();
        ZonedDateTimeSerializer zonedDateTimeSerializer = new ZonedDateTimeSerializer(new DateTimeFormatterBuilder().appendInstant(0).toFormatter());
        module.addSerializer(ZonedDateTime.class, zonedDateTimeSerializer);
        module.addDeserializer(ZonedDateTime.class, InstantDeserializer.ZONED_DATE_TIME);

        objectMapper = Jackson2ObjectMapperBuilder.json()
                .modules(module)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .build();
    }

    @Test
    public void serialize() throws IOException {
        ZonedDateTime zonedDateTime = ZonedDateTime.now();
        String noMillis = objectMapper.writeValueAsString(zonedDateTime);
        System.out.print(noMillis);
    }

    @Test
    public void deserialize() throws IOException {
        String dateTime = "\"2017-10-26T12:54:59Z\"";
        ZonedDateTime noMillis = objectMapper.readValue(dateTime, ZonedDateTime.class);
        System.out.print(noMillis);
    }
}

Here's some Kotlin code of formatting Instant fields, so they will not contain milliseconds, you can use custom date formatters 这里有一些格式化Instant字段的Kotlin代码,因此它们不包含毫秒,您可以使用自定义日期格式化程序

ObjectMapper().apply {
        val javaTimeModule = JavaTimeModule()
        javaTimeModule.addSerializer(Instant::class.java, Iso8601WithoutMillisInstantSerializer())
        registerModule(javaTimeModule)
        disable(WRITE_DATES_AS_TIMESTAMPS)
    }

private class Iso8601WithoutMillisInstantSerializer
        : InstantSerializer(InstantSerializer.INSTANCE, false, DateTimeFormatterBuilder().appendInstant(0).toFormatter())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM