web-dev-qa-db-ja.com

objectMapperを使用してJava.time.Instantの文字列の形式を設定するにはどうすればよいですか?

作成されたデータフィールドに_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_を使用して、JSONとしてElasticsearchにアイテムを保存しています:

_bulkRequestBody.append(objectMapper.writeValueAsString(item));
_

ObjectMapperは、このフィールドをオブジェクトとしてシリアル化します。

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

アノテーション@JsonFormat(shape = JsonFormat.Shape.STRING)を試していましたが、うまくいきません。

私の質問は、このフィールドを_2010-05-30 22:15:52_文字列としてシリアル化する方法ですか?

18

1つの解決策は、 jackson-modules-Java8 を使用することです。その後、JavaTimeModuleをオブジェクトマッパーに追加できます。

ObjectMapper objectMapper = new ObjectMapper();

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

デフォルトでは、Instantはエポック値(単一の数値の秒とナノ秒)としてシリアル化されます:

{"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"}

詳細の1つは、シリアル化された文字列で、秒の端数(小数点以下のすべて)を破棄することです。そのため、逆シリアル化する場合、この情報は復元できません(ゼロに設定されます)。

上記の例では、元のInstant2017-08-14T12:17:47.720Zですが、シリアル化された文字列は2017-08-14 12:17:47(秒の小数部なし)であるため、逆シリアル化されると結果のInstant2017-08-14T12:17:47Z.720ミリ秒が失われます)。

37
user7605325

Java 8タイムスタンプ。POMのjackson-datatype-jsr310の最新バージョンが必要で、次のモジュールを登録してください。

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);
}
4
UltimaWeapon

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

JavaTimeModuleですでに設定されているSpring ObjectMapperを使用できます。 Springコンテキストから挿入するだけで、new ObjectMapper()を使用しないでください。

1
Valeriy K.

私の場合、JavaTimeModuleを登録するだけで十分でした:

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

  messageObject = objectMapper.writeValueAsString(event);

イベントObjectには、Instant型のフィールドがあります。

逆シリアル化では、Java timeモジュールを登録する必要もあります。

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

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