web-dev-qa-db-ja.com

Jacksonを使用してInstantをISO8601にシリアル化するときにミリ秒を強制する

_Spring Boot 2.0.0.M6_、_Spring Framework 5.0.1.RELEASE_、および_Jackson 2.9.2_を使用するプロジェクトでJacksonを使用したJSONシリアル化に関連するいくつかの質問があります。

_application.properties_で次のジャクソン関連の設定を構成しました。

_spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
_

シリアル化は主に必要に応じて機能します。それでも、ジャクソンが_000_の場合、ミリ秒をカットオフしているように見えることに気づきました。

テスト1:ミリ秒を_000_:に設定してインスタントをシリアル化する

  • Instant.parse("2017-09-14T04:28:48.000Z")を使用してインスタントフィールドを初期化します
  • Jacksonを使用してシリアル化します
  • 出力は_"2017-09-14T04:28:48Z"_になります

テスト2:ミリ秒を_000_以外の値に設定してインスタントをシリアル化:

  • Instant.parse("2017-09-14T04:28:48.100Z")を使用してインスタントフィールドを初期化します
  • Jacksonを使用してシリアル化します
  • 出力は_"2017-09-14T04:28:48.100Z"_になります

質問

  • その動作は設計によるものですか?
  • _000_のシリアル化を強制するためにできることはありますか?
10
André Gasser

これにはジャクソンの問題が開いているようです ここ *。そのリンクには2つの回避策が含まれています

回避策1

 ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.registerModule(new JavaTimeModule());
    SimpleModule module = new SimpleModule();
    module.addSerializer(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
        @Override
        public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeString(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ").format(zonedDateTime));
        }
    });
    objectMapper.registerModule(module);
    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.enable(SerializationFeature.INDENT_OUTPUT);

回避策2

JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(ZonedDateTime.class,
  new ZonedDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")));
ObjectMapper mapper = new ObjectMapper().registerModule(javaTimeModule);

* FasterXML/jackson-datatype-jsr310が非推奨になり、 jackson-modules-Java8 に移動されたため、リンクは無効になっています。 https://github.com/FasterXML/jackson-modules-Java8/issues/76 を参照してください

3
Sean Carroll

私はこのアプローチを使用して解決します:

ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(Instant.class, new InstantSerializerWithMilliSecondPrecision());
objectMapper.registerModule(module);

そしてInstantSerializerWithMilliSecondPrecisionのために私はこれを使用しました:

public class InstantSerializerWithMilliSecondPrecision extends InstantSerializer {

    public InstantSerializerWithMilliSecondPrecision() {
        super(InstantSerializer.INSTANCE, false, new DateTimeFormatterBuilder().appendInstant(3).toFormatter());
    }
}

現在、インスタントシリアル化には常にミリ秒が含まれています。例:2019-09-27T02:59:59.000Z

2
Gustavo

ショーンキャロルが言及した2つの回避策のどれも私にはうまくいきません。結局、Instant用に独自のシリアライザーを作成することになります。

_final ObjectMapper mapper = new ObjectMapper();
final JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Instant.class, new KeepMillisecondInstantSerializer());
mapper.registerModule(javaTimeModule);

public class KeepMillisecondInstantSerializer extends JsonSerializer<Instant> {

    private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSX")
            .withZone(ZoneId.of("UTC"));

    @Override
    public void serialize(final Instant instant, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException {
        final String serializedInstant = dateTimeFormatter.format(instant);
        jsonGenerator.writeString(serializedInstant);
    }
}
_

Jackson Instant.toString()メソッドを使用してInstantオブジェクトをシリアル化する デフォルトで。私はまた StackOverflowのInstant.toString()メソッドに関するいくつかの議論 を見つけます。

1
Sher10ck