J'ai une entité avec Java.time.Instant
pour le champ de données créé:
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Item {
private String id;
private String url;
private Instant createdDate;
}
J'utilise com.fasterxml.jackson.databind.ObjectMapper
pour enregistrer l'élément dans Elasticsearch en tant que JSON:
bulkRequestBody.append(objectMapper.writeValueAsString(item));
ObjectMapper
sérialise ce champ sous forme d'objet:
"createdDate": {
"epochSecond": 1502643595,
"nano": 466000000
}
J'essayais l'annotation @JsonFormat(shape = JsonFormat.Shape.STRING)
mais cela ne fonctionne pas pour moi.
Ma question est de savoir comment je pourrais sérialiser ce champ en tant que chaîne 2010-05-30 22:15:52
?
Une solution consiste à utiliser jackson-modules-Java8 . Ensuite, vous pouvez ajouter une JavaTimeModule
à votre mappeur d'objets:
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
Par défaut, Instant
est sérialisé en tant que valeur Epoch (secondes et nanosecondes dans un nombre unique):
{"createdDate":1502713067.720000000}
Vous pouvez changer cela en définissant dans le mappeur d'objets:
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Cela produira la sortie:
{"createdDate":"2017-08-14T12:17:47.720Z"}
Les deux formats ci-dessus sont désérialisés sans configuration supplémentaire.
Pour changer le format de la sérialisation, ajoutez simplement une annotation JsonFormat
au champ:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "UTC")
private Instant createdDate;
Vous devez définir le fuseau horaire, sinon la Instant
ne peut pas être sérialisée correctement (une exception est générée). La sortie sera:
{"createdDate":"2017-08-14 12:17:47"}
Si vous ne voulez pas (ou ne pouvez pas) utiliser les modules Java8, vous pouvez également créer un sérialiseur et un désérialiseur personnalisés, à l’aide du 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()));
}
}
Ensuite, vous annotez le champ avec ces classes personnalisées:
@JsonDeserialize(using = MyCustomDeserializer.class)
@JsonSerialize(using = MyCustomSerializer.class)
private Instant createdDate;
La sortie sera:
{"createdDate":"2017-08-14 12:17:47"}
Un détail est que, dans la chaîne sérialisée, vous supprimez la fraction de seconde (tout ce qui suit le point décimal). Ainsi, lors de la désérialisation, cette information ne peut pas être récupérée (elle sera mise à zéro).
Dans l'exemple ci-dessus, la Instant
d'origine est 2017-08-14T12:17:47.720Z
, mais la chaîne sérialisée est 2017-08-14 12:17:47
(sans la fraction de secondes). Ainsi, lors de la désérialisation, la Instant
résultante est 2017-08-14T12:17:47Z
(les .720
millisecondes sont perdues).
Voici un code Kotlin de formatage Instant
, de sorte qu'il ne contient pas des millisecondes, vous pouvez utiliser des formateurs de date personnalisés
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())
Vous devez ajouter ci-dessous la dépendance
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.6.5</version>
</dependency>
Et puis enregistrez les modules comme ci-dessous:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
Pour ceux qui cherchent à analyser les timestamps Java 8. Vous avez besoin d'une version récente de jackson-datatype-jsr310
dans votre POM et le module suivant est enregistré:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Pour tester ce code
@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);
}
Dans mon cas, il suffisait d'enregistrer le JavaTimeModule:
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
objectMapper.registerModule(module);
messageObject = objectMapper.writeValueAsString(event);
Dans le cas objet, j'ai un champ de type Instant.
Dans la désérialisation, vous devez également enregistrer le module de temps Java:
ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
Event event = objectMapper.readValue(record.value(), Event.class);