Джексон, 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);
}
}