Jackson Date

Jackson Date

1. Visão geral

Neste tutorial, serializaremos datas com Jackson. Começaremos serializando um java.util.Date simples, depois Joda-Time e também o Java 8DateTime.

2. SerializarDate para carimbo de data / hora

Primeiro - vamos ver como serializar umjava.util.Date with Jackson simples.

No exemplo a seguir - serializaremos uma instância de “Event” que tem um campoDateeventDate“:

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

O que é importante aqui é que Jackson serializará a data em um formato de carimbo de data / hora por padrão (número de milissegundos desde 1º de janeiro de 1970, UTC).

A saída real da serialização “event” é:

{
   "name":"party",
   "eventDate":3600000
}

3. SerializarDate para ISO-8601

Serializar para esse formato de carimbo de data e hora conciso não é ideal. Vamos agora serializar oDate para o formatoISO-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"));
}

Observe como a representação da data agora é muito mais legível.

4. ConfigureObjectMapperDateFormat

As soluções anteriores ainda carecem de total flexibilidade para escolher o formato exato para representar as instânciasjava.util.Date.

Vamos agora dar uma olhada em uma configuração que nos permitiráset 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));
}

Observe que, embora agora sejamos mais flexíveis em relação ao formato de data - ainda estamos usando uma configuração global no nível deObjectMapper inteiro.

5. Use@JsonFormat para formatarDate

A seguir, vamos dar uma olhada na anotação@JsonFormat paracontrol the date format on individual classes em vez de globalmente, para todo o aplicativo:

public class Event {
    public String name;

    @JsonFormat
      (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}

Agora - vamos testar:

@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. SerializadorDate personalizado

Em seguida - para obter controle total sobre a saída, vamos aproveitar um serializador personalizado para datas:

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

A seguir - vamos usá-lo como o serializador de nosso campo “eventDate”:

public class Event {
    public String name;

    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}

Finalmente - vamos testar:

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

Leitura adicional:

Como serializar e desserializar enums com Jackson

Como serializar e desserializar um Enum como um objeto JSON usando o Jackson 2.

Read more

Jackson - Serializador personalizado

Controle sua saída JSON com o Jackson 2 usando um serializador personalizado.

Read more

Introdução à desserialização personalizada em Jackson

Use Jackson para mapear JSON personalizado para qualquer gráfico de entidade java com controle total sobre o processo de desserialização.

Read more

7. Serializar Joda-Time com Jackson

As datas nem sempre são uma instância dejava.util.Date; na verdade - eles são cada vez mais representados por alguma outra classe - e um comum é, claro, a implementaçãoDateTime da biblioteca Joda-Time.

Vamos ver como podemosserialize DateTime with Jackson.

Faremos uso do módulojackson-datatype-joda para suporte Joda-Time pronto para uso:


  com.fasterxml.jackson.datatype
  jackson-datatype-joda
  2.9.7

E agora podemos simplesmente registrar oJodaModulee pronto:

@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. Serializar JodaDateTime com serializador personalizado

Se não quisermos a dependência Joda-Time Jackson extra - também podemos usara custom serializer (semelhante aos exemplos anteriores) para obter instâncias deDateTime serializadas de forma limpa:

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

A seguir - vamos usá-lo como nosso serializador "eventDate" de propriedade:

public class Event {
    public String name;

    @JsonSerialize(using = CustomDateTimeSerializer.class)
    public DateTime eventDate;
}

Finalmente - vamos colocar tudo junto e testar:

@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. Serializar Java 8Date com Jackson

A seguir - vamos ver como serializar Java 8DateTime - neste exemplo,LocalDateTime – using Jackson. Podemos fazer uso do módulojackson-datatype-jsr310:


    com.fasterxml.jackson.datatype
    jackson-datatype-jsr310
    2.9.7

Agora, tudo o que precisamos fazer é registrarJavaTimeModule (JSR310Module está obsoleto) e Jackson cuidará do resto:

@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. Serializar Java 8Date sem qualquer dependência extra

Se você não quiser a dependência extra, você sempre pode usara 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));
    }
}

A seguir - vamos usar o serializador para nosso campo “eventDate”:

public class Event {
    public String name;

    @JsonSerialize(using = CustomLocalDateTimeSerializer.class)
    public LocalDateTime eventDate;
}

Agora - vamos testar:

@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. DesserializarDate

A seguir - vamos ver como desserializar aDate comJackson. No exemplo a seguir - desserializamos uma instância “Event” contendo uma data:

@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. Desserializar JodaZonedDateTime com fuso horário preservado

Em sua configuração padrão, Jackson ajusta o fuso horário de um JodaZonedDateTime ao fuso horário do contexto local. Como, por padrão, o fuso horário do contexto local não está definido e precisa ser configurado manualmente, Jackson ajusta o fuso horário para 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));
}

Este caso de teste falhará com a saída:

serialized: 2017-08-14T13:52:22.071+02:00[Europe/Berlin]
restored: 2017-08-14T11:52:22.071Z[UTC]

Felizmente, existe uma solução rápida e simples para esse estranho comportamento padrão:we just have to tell Jackson, not to adjust the time zone.

Isso pode ser feito adicionando a linha de código abaixo ao caso de teste acima:

objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

Observe que, para preservar o fuso horário, também precisamos desativar o comportamento padrão de serializar a data para o registro de data e hora.

13. DeserializadorDate personalizado

Vejamos também como usara custom Date deserializer; vamos escrever um desserializador personalizado para a propriedade “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);
        }
    }
}

A seguir - vamos usá-lo como o desserializador “eventDate”:

public class Event {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

E finalmente - vamos testar:

@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. Conclusão

Neste extenso artigoDate, observamos as formas mais relevantesJackson can help marshalli and unmarshall a date to JSON usando um formato sensato sobre o qual temos controle.

A implementação de todos esses exemplos e trechos de códigocan be found in my GitHub project - este é um projeto baseado em Maven, portanto, deve ser fácil de importar e executar como está.