Джексон, java.time, ISO 8601, сериализация без миллисекунд

Я использую Jackson 2.8 и нуждаюсь в общении с API, который не позволяет миллисекунды в пределах временных меток ISO 8601.

Ожидаемый формат: "2016-12-24T00:00:00Z"

Я использую Jackson JavaTimeModule с WRITE_DATES_AS_TIMESTAMPS, установленным в false.

Но это будет печатать миллисекунды.

Итак, я попытался использовать objectMapper.setDateFormat, который ничего не изменил.

Мое текущее обходное решение таково:

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

Я переопределяю сериализатор по умолчанию для Instant.class, который работает.


Есть ли какой-нибудь хороший способ, используя какой-либо параметр конфигурации, чтобы решить эту проблему?

Ответ 1

Обновление:

Просто добавьте аннотацию @JsonFormat с форматом даты над свойством Instant. Это очень просто.

В случае, если у вас есть ObjectMapper с JavaTimeModule следующим образом:

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

Если у вас есть класс с свойством Instant, вы должны добавить аннотацию @JsonFormat и поместить шаблон даты, который не имеет миллисекунд. Это было бы следующим:

public static class TestDate {

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

    //getters & setters
}

Итак, если вы сериализуете объект в Json, он отлично работает:

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

Выход

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


Старый ответ. Я не знаю, почему, но он не работает должным образом:

Вы можете использовать Jackson2ObjectMapperBuilder для его создания.

Вам просто нужно добавить форму dateFormat. Это будет что-то вроде следующего:

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();

Ответ 2

Вот альтернатива, которую вы можете установить глобально, но вам понадобится использовать ZonedDateTime с мгновенным форматированием, поскольку мы не можем установить формат для Instant Сериализатора, снабженного Java Time Module.

Вы не увидите каких-либо побочных эффектов, связанных с использованием зонированной даты для моментального запуска, поскольку джексон выполняет сериализацию идентификатора зоны отдельно и по умолчанию отключен. Так что технически это похоже на применение форматирования до Instant.

При использовании этого способа сериализатор 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);
    }
}