Date de Jackson

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.

Read more

Jackson - Sérialiseur personnalisé

Contrôlez votre sortie JSON avec Jackson 2 à l'aide d'un sérialiseur personnalisé.

Read more

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.

Read more

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.