Date de Jackson
1. Vue d'ensemble
Dans ce didacticiel, nous allons sérialiser les dates avec Jackson. Nous allons commencer par sérialiser un simple java.util.Date, puis Joda-Time ainsi que les Java 8DateTime.
2. SérialiserDate en horodatage
Tout d'abord, voyons comment sérialiser un simplejava.util.Date with Jackson.
Dans l'exemple suivant - nous sérialiserons une instance de «Event» qui a un champDate «eventDate»:
@Test
public void whenSerializingDateWithJackson_thenSerializedToTimestamp()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = df.parse("01-01-1970 01:00");
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
mapper.writeValueAsString(event);
}
Ce qui est important ici, c'est que Jackson sérialisera la date dans un format d'horodatage par défaut (nombre de millisecondes depuis le 1er janvier 1970, UTC).
La sortie réelle de la sérialisation «event» est:
{
"name":"party",
"eventDate":3600000
}
3. SérialiserDate selon ISO-8601
La sérialisation dans ce format d'horodatage concis n'est pas optimale. Sérialisons maintenant lesDate au formatISO-8601:
@Test
public void whenSerializingDateToISO8601_thenSerializedToText()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String toParse = "01-01-1970 02:30";
Date date = df.parse(toParse);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// StdDateFormat is ISO8601 since jackson 2.9
mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));
String result = mapper.writeValueAsString(event);
assertThat(result, containsString("1970-01-01T02:30:00.000+00:00"));
}
Notez que la représentation de la date est maintenant beaucoup plus lisible.
4. ConfigurerObjectMapperDateFormat
Les solutions précédentes n'ont toujours pas la flexibilité totale de choisir le format exact pour représenter les instancesjava.util.Date.
Jetons maintenant un œil à une configuration qui nous permettra deset our formats for representing dates:
@Test
public void whenSettingObjectMapperDateFormat_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
String toParse = "20-12-2014 02:30";
Date date = df.parse(toParse);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(df);
String result = mapper.writeValueAsString(event);
assertThat(result, containsString(toParse));
}
Notez que, même si nous sommes désormais plus flexibles en ce qui concerne le format de la date, nous utilisons toujours une configuration globale au niveau de l'ensemble desObjectMapper.
5. Utilisez@JsonFormat pour formaterDate
Examinons ensuite l'annotation@JsonFormat àcontrol the date format on individual classes plutôt que globalement, pour l'ensemble de l'application:
public class Event {
public String name;
@JsonFormat
(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
public Date eventDate;
}
Maintenant, testons-le:
@Test
public void whenUsingJsonFormatAnnotationToFormatDate_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(event);
assertThat(result, containsString(toParse));
}
6. Sérialiseur personnaliséDate
Ensuite, pour avoir un contrôle total sur la sortie, nous allons utiliser un sérialiseur personnalisé pour les dates:
public class CustomDateSerializer extends StdSerializer {
private SimpleDateFormat formatter
= new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class t) {
super(t);
}
@Override
public void serialize (Date value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
Ensuite, utilisons-le comme sérialiseur de notre champ "eventDate":
public class Event {
public String name;
@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}
Enfin, testons-le:
@Test
public void whenUsingCustomDateSerializer_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(event);
assertThat(result, containsString(toParse));
}
Lectures complémentaires:
Comment sérialiser et désérialiser des enums avec Jackson
Comment sérialiser et désérialiser un Enum en tant qu’objet JSON avec Jackson 2.
Jackson - Sérialiseur personnalisé
Contrôlez votre sortie JSON avec Jackson 2 à l'aide d'un sérialiseur personnalisé.
Débuter avec la désérialisation personnalisée à Jackson
Utilisez Jackson pour mapper le JSON personnalisé à n’importe quel graphe d’entités java avec un contrôle total sur le processus de désérialisation.
7. Sérialiser Joda-Time avec Jackson
Les dates ne sont pas toujours une instance dejava.util.Date; en fait - elles sont de plus en plus représentées par une autre classe - et la plus courante est, bien sûr, l'implémentation deDateTime de la bibliothèque Joda-Time.
Voyons comment nous pouvonsserialize DateTime with Jackson.
Nous allons utiliser le modulejackson-datatype-joda pour le support Joda-Time prêt à l'emploi:
com.fasterxml.jackson.datatype
jackson-datatype-joda
2.9.7
Et maintenant, nous pouvons simplement enregistrer lesJodaModule et faire:
@Test
public void whenSerializingJodaTime_thenCorrect()
throws JsonProcessingException {
DateTime date = new DateTime(2014, 12, 20, 2, 30,
DateTimeZone.forID("Europe/London"));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String result = mapper.writeValueAsString(date);
assertThat(result, containsString("2014-12-20T02:30:00.000Z"));
}
8. Sérialiser JodaDateTime avec le sérialiseur personnalisé
Si nous ne voulons pas de la dépendance supplémentaire Joda-Time Jackson, nous pouvons également utilisera custom serializer (similaire aux exemples précédents) pour que les instances deDateTime soient sérialisées proprement:
public class CustomDateTimeSerializer extends StdSerializer {
private static DateTimeFormatter formatter =
DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
public CustomDateTimeSerializer() {
this(null);
}
public CustomDateTimeSerializer(Class t) {
super(t);
}
@Override
public void serialize
(DateTime value, JsonGenerator gen, SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.print(value));
}
}
Ensuite, utilisons-le comme sérialiseur de propriété "eventDate":
public class Event {
public String name;
@JsonSerialize(using = CustomDateTimeSerializer.class)
public DateTime eventDate;
}
Enfin, mettons tout ensemble et testons-le:
@Test
public void whenSerializingJodaTimeWithJackson_thenCorrect()
throws JsonProcessingException {
DateTime date = new DateTime(2014, 12, 20, 2, 30);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(event);
assertThat(result, containsString("2014-12-20 02:30"));
}
9. Sérialiser Java 8Date avec Jackson
Ensuite, voyons comment sérialiser Java 8DateTime - dans cet exemple,LocalDateTime – using Jackson. Nous pouvons utiliser le modulejackson-datatype-jsr310:
com.fasterxml.jackson.datatype
jackson-datatype-jsr310
2.9.7
Maintenant, tout ce que nous avons à faire est d'enregistrer lesJavaTimeModule (JSR310Module est obsolète) et Jackson s'occupera du reste:
@Test
public void whenSerializingJava8Date_thenCorrect()
throws JsonProcessingException {
LocalDateTime date = LocalDateTime.of(2014, 12, 20, 2, 30);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String result = mapper.writeValueAsString(date);
assertThat(result, containsString("2014-12-20T02:30"));
}
10. Sérialisation de Java 8Date sans aucune dépendance supplémentaire
Si vous ne voulez pas de dépendance supplémentaire, vous pouvez toujours utilisera custom serializer to write out the Java 8 DateTime to JSON:
public class CustomLocalDateTimeSerializer
extends StdSerializer {
private static DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
public CustomLocalDateTimeSerializer() {
this(null);
}
public CustomLocalDateTimeSerializer(Class t) {
super(t);
}
@Override
public void serialize(
LocalDateTime value,
JsonGenerator gen,
SerializerProvider arg2)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
Ensuite, utilisons le sérialiseur pour notre champ "eventDate":
public class Event {
public String name;
@JsonSerialize(using = CustomLocalDateTimeSerializer.class)
public LocalDateTime eventDate;
}
Maintenant, testons-le:
@Test
public void whenSerializingJava8DateWithCustomSerializer_thenCorrect()
throws JsonProcessingException {
LocalDateTime date = LocalDateTime.of(2014, 12, 20, 2, 30);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(event);
assertThat(result, containsString("2014-12-20 02:30"));
}
11. DésérialiserDate
Ensuite, voyons comment désérialiser unDate avecJackson. Dans l'exemple suivant, nous désérialisons une instance «Event» contenant une date:
@Test
public void whenDeserializingDateWithJackson_thenCorrect()
throws JsonProcessingException, IOException {
String json = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(df);
Event event = mapper.readerFor(Event.class).readValue(json);
assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}
12. Désérialiser JodaZonedDateTime avec le fuseau horaire préservé
Dans sa configuration par défaut, Jackson ajuste le fuseau horaire d'un JodaZonedDateTime au fuseau horaire du contexte local. Comme, par défaut, le fuseau horaire du contexte local n'est pas défini et doit être configuré manuellement, Jackson ajuste le fuseau horaire à GMT:
@Test
public void whenDeserialisingZonedDateTimeWithDefaults_thenNotCorrect()
throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Europe/Berlin"));
String converted = objectMapper.writeValueAsString(now);
ZonedDateTime restored = objectMapper.readValue(converted, ZonedDateTime.class);
System.out.println("serialized: " + now);
System.out.println("restored: " + restored);
assertThat(now, is(restored));
}
Ce cas de test échouera avec la sortie:
serialized: 2017-08-14T13:52:22.071+02:00[Europe/Berlin]
restored: 2017-08-14T11:52:22.071Z[UTC]
Heureusement, il existe une solution rapide et simple pour ce comportement par défaut étrange:we just have to tell Jackson, not to adjust the time zone.
Cela peut être fait en ajoutant la ligne de code ci-dessous au scénario de test ci-dessus:
objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
Notez que, pour préserver le fuseau horaire, nous devons également désactiver le comportement par défaut de la sérialisation de la date sur l'horodatage.
13. Désérialiseur personnaliséDate
Voyons également comment utilisera custom Date deserializer; nous allons écrire un désérialiseur personnalisé pour la propriété "eventDate":
public class CustomDateDeserializer extends StdDeserializer {
private SimpleDateFormat formatter =
new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class> vc) {
super(vc);
}
@Override
public Date deserialize(JsonParser jsonparser, DeserializationContext context)
throws IOException, JsonProcessingException {
String date = jsonparser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Ensuite, utilisons-le comme désérialiseur «eventDate»:
public class Event {
public String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
public Date eventDate;
}
Et enfin - testons-le:
@Test
public void whenDeserializingDateUsingCustomDeserializer_thenCorrect()
throws JsonProcessingException, IOException {
String json = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
ObjectMapper mapper = new ObjectMapper();
Event event = mapper.readerFor(Event.class).readValue(json);
assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}
14. Conclusion
Dans cet article complet surDate, nous avons examiné les moyens les plus pertinents pourJackson can help marshalli and unmarshall a date to JSON en utilisant un format sensible sur lequel nous contrôlons.
L'implémentation de tous ces exemples et extraits de codecan be found in my GitHub project - il s'agit d'un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.