webentwicklung-frage-antwort-db.com.de

Wie kann ich das String-Format für Java.time.Instant mit objectMapper einstellen?

Ich habe eine Entität mit Java.time.Instant für das erstellte Datenfeld:

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

Ich verwende com.fasterxml.jackson.databind.ObjectMapper, um Artikel als JSON in Elasticsearch zu speichern:

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

ObjectMapper serialisiert dieses Feld als Objekt: 

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

Ich habe die Annotation @JsonFormat(shape = JsonFormat.Shape.STRING) ausprobiert, funktioniert aber nicht für mich.

Meine Frage ist, wie ich dieses Feld als 2010-05-30 22:15:52-String serialisieren könnte.

9

Eine Lösung ist die Verwendung von jackson-modules-Java8 . Dann können Sie Ihrem Object Mapper einen JavaTimeModule hinzufügen:

ObjectMapper objectMapper = new ObjectMapper();

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

Standardmäßig wird Instant als Epoch-Wert serialisiert (Sekunden und Nanosekunden in einer einzelnen Zahl):

{"createdDate":1502713067.720000000}

Sie können dies ändern, indem Sie in der Objektübersicht festlegen:

objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

Dies erzeugt die Ausgabe:

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

Beide Formate werden ohne weitere Konfiguration deserialisiert.

Um das Serialisierungsformat zu ändern, fügen Sie dem Feld einfach eine JsonFormat-Anmerkung hinzu:

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

Sie müssen die Zeitzone einstellen, andernfalls kann Instant nicht ordnungsgemäß serialisiert werden (es wird eine Ausnahme ausgelöst). Die Ausgabe wird sein:

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

Wenn Sie keine Java8-Module verwenden möchten (oder können), können Sie einen benutzerdefinierten Serializer und Deserializer mit einem Java.time.format.DateTimeFormatter erstellen:

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

Dann kommentieren Sie das Feld mit diesen benutzerdefinierten Klassen:

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

Die Ausgabe wird sein:

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

Ein Detail ist, dass Sie in der serialisierten Zeichenfolge den Sekundenbruchteil (alles nach dem Dezimalpunkt) verwerfen. Bei der Deserialisierung können diese Informationen daher nicht wiederhergestellt werden (sie werden auf Null gesetzt).

Im obigen Beispiel ist der ursprüngliche Instant2017-08-14T12:17:47.720Z, aber die serialisierte Zeichenfolge ist 2017-08-14 12:17:47 (ohne den Bruchteil von Sekunden). Wenn also dererialisiert wird, lautet der Instant2017-08-14T12:17:47Z (die .720 Millisekunden gehen verloren).

17
user7605325

Hier ist ein Kotlin-Formatierungscode Instant, damit er keine Millisekunden enthält. Sie können benutzerdefinierte Datumsformatierer verwenden

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

Sie müssen unten Abhängigkeit hinzufügen

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

Registrieren Sie dann die Module wie folgt:

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

Für diejenigen, die Java 8-Zeitstempel analysieren möchten. Sie benötigen eine aktuelle Version von jackson-datatype-jsr310 in Ihrem POM und haben das folgende Modul registriert:

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

Um diesen Code zu testen

@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

Sie können Spring ObjectMapper verwenden, der bereits mit JavaTimeModule konfiguriert wurde. Fügen Sie es einfach aus dem Spring-Kontext ein und verwenden Sie new ObjectMapper() nicht.

1
Valeriy K.

In meinem Fall war es ausreichend, das JavaTimeModule zu registrieren:

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

  messageObject = objectMapper.writeValueAsString(event);

Für den Fall Objekt habe ich ein Feld vom Typ Instant.

Bei der Deserialisierung müssen Sie auch das Java-Zeitmodul registrieren:

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

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