it-roy-ru.com

Как установить формат строки для Java.time.Instant, используя objectMapper?

У меня есть объект с Java.time.Instant для созданного поля данных:

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Item {
    private String id;
    private String url;
    private Instant createdDate;
}

Я использую com.fasterxml.jackson.databind.ObjectMapper для сохранения элемента в Elasticsearch как JSON:

bulkRequestBody.append(objectMapper.writeValueAsString(item));

ObjectMapper сериализует это поле как объект: 

"createdDate": {
    "epochSecond": 1502643595,
    "nano": 466000000
}

Я пробовал аннотацию @JsonFormat(shape = JsonFormat.Shape.STRING), но она не работает для меня.

У меня вопрос, как я мог сериализовать это поле как строку 2010-05-30 22:15:52?

9
Uladzislau Kaminski

Одним из решений является использование jackson-modules-Java8 . Затем вы можете добавить JavaTimeModule в ваш объект сопоставления:

ObjectMapper objectMapper = new ObjectMapper();

JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);

По умолчанию Instant сериализуется как значение Epoch (секунды и наносекунды в одном числе):

{"createdDate":1502713067.720000000}

Вы можете изменить это, установив в сопоставителе объектов:

objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

Это даст результат:

{"createdDate":"2017-08-14T12:17:47.720Z"}

Оба указанных выше формата десериализованы без какой-либо дополнительной настройки.

Чтобы изменить формат сериализации, просто добавьте аннотацию JsonFormat в поле:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
private Instant createdDate;

Вам нужно установить часовой пояс, иначе Instant не может быть правильно сериализован (он выдает исключение). Выход будет:

{"createdDate":"2017-08-14 12:17:47"}

Другой вариант, если вы не хотите (или не можете) использовать модули Java8, - это создать собственный сериализатор и десериализатор, используя Java.time.format.DateTimeFormatter:

public class MyCustomSerializer extends JsonSerializer<Instant> {

    private DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);

    @Override
    public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
        String str = fmt.format(value);

        gen.writeString(str);
    }
}

public class MyCustomDeserializer extends JsonDeserializer<Instant> {

    private DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);

    @Override
    public Instant deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        return Instant.from(fmt.parse(p.getText()));
    }
}

Затем вы аннотируете поле этими пользовательскими классами:

@JsonDeserialize(using = MyCustomDeserializer.class)
@JsonSerialize(using = MyCustomSerializer.class)
private Instant createdDate;

Результат будет:

{"createdDate":"2017-08-14 12:17:47"}

Одна деталь заключается в том, что в сериализованной строке вы отбрасываете долю секунды (все после десятичной точки). Таким образом, при десериализации эта информация не может быть восстановлена ​​(она будет установлена ​​на ноль).

В приведенном выше примере исходной переменной Instant является 2017-08-14T12:17:47.720Z, но сериализованной строкой является 2017-08-14 12:17:47 (без доли секунд), поэтому при десериализации полученная Instant будет 2017-08-14T12:17:47Z (миллисекунды .720 теряются).

17
user7605325

Вот код Kotlin для форматирования Instant, поэтому он не содержит миллисекунд, вы можете использовать собственные средства форматирования даты

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())
2
ZZ 5

Вы должны добавить ниже зависимости

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.5</version>
</dependency>

А затем зарегистрируйте модули, как показано ниже:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
2
dassum

Для тех, кто хочет разобрать временные метки Java 8. Вам нужна последняя версия jackson-datatype-jsr310 в вашем POM и зарегистрирован следующий модуль:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

Чтобы проверить этот код

@Test
void testSeliarization() throws IOException {
    String expectedJson = "{\"parseDate\":\"2018-12-04T18:47:38.927Z\"}";
    MyPojo pojo = new MyPojo(ZonedDateTime.parse("2018-12-04T18:47:38.927Z"));

    // serialization
    assertThat(objectMapper.writeValueAsString(pojo)).isEqualTo(expectedJson);

    // deserialization
    assertThat(objectMapper.readValue(expectedJson, MyPojo.class)).isEqualTo(pojo);
}
2
UltimaWeapon

В моем случае достаточно было зарегистрировать JavaTimeModule:

  ObjectMapper objectMapper = new ObjectMapper();
  JavaTimeModule module = new JavaTimeModule();
  objectMapper.registerModule(module);

  messageObject = objectMapper.writeValueAsString(event);

В объекте события у меня есть поле типа Instant.

В десериализации также необходимо зарегистрировать модуль времени Java:

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

Event event = objectMapper.readValue(record.value(), Event.class);
0
Laura Liparulo