Spring custom (des) serialização de data e hora

Imagine uma situação bastante comum: seu aplicativo interage com clientes que estão em fusos horários diferentes. Freqüentemente, você precisa trabalhar com datas e, para que o sistema funcione corretamente, elas são enviadas com o fuso horário do remetente. Ao fazer isso, você precisa:





  1. Quando uma solicitação for recebida, traga a data para o horário do servidor e trabalhe com ela, e também salve-a no banco de dados neste formulário





  2. Em resposta, retorne a data e hora indicando o fuso horário do servidor





Para fazer isso, o Spring fornece um mecanismo conveniente para escrever serialização e desserialização customizadas. Sua principal vantagem é a capacidade de mover as conversões de data (e outros tipos de dados) para uma classe de configuração separada e não chamar métodos de conversão todas as vezes no código-fonte.





Desserialização

Para que o Spring entenda que é nossa classe que precisa ser usada para (des) serialização, ela deve ser marcada com a anotação @JsonComponent







Bem, para manter o código o mais conciso possível, usarei uma classe estática interna, que deve ser herdada JsonDeserializer



e parametrizada com o tipo de dados de que precisamos. Uma vez que JsonDeserializer



 é uma classe abstrata, precisamos substituir seu método abstratodeserialize()







@JsonComponent
public class CustomDateSerializer {  
  
    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    
        @Override
        public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
            return null;
        }
    }
}
      
      



A partir dos parâmetros do método, obtemos a string passada pelo cliente, verificamos se há nulo e obtemos um objeto de classe a partir dela ZonedDateTime







public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
    String date = jsonParser.getText();
    if (date.isEmpty() || isNull(date) {
        return null;
    }
    ZonedDateTime userDateTime = ZonedDateTime.parse(date);
}
      
      



, userDateTime



withZoneSameInstant()



. LocalDateTime







, , , . , .





public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

    @Override
    public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        String date = jsonParser.getText();
        if (date.isEmpty()) {
            return null;
        }
        try {
            ZonedDateTime userDateTime = ZonedDateTime.parse(date);
            ZonedDateTime serverTime = userDateTime.withZoneSameInstant(ZoneId.systemDefault());
            return serverTime.toLocalDateTime();
        } catch (DateTimeParseException e) {
            try {
                return LocalDateTime.parse(date);
            } catch (DateTimeParseException ex) {
                throw new IllegalArgumentException("Error while parsing date", ex);
            }
        }
    }
}
      
      



,  UTC+03. , 2021-01-21T22:00:00+07:00



,





public class Subscription {

    private LocalDateTime startDate;
  
    // standart getters and setters
}
      
      



@RestController 
public class TestController {
  
  @PostMapping
  public void process(@RequestBody Subscription subscription) {
    //     startDate  subscription   2021-01-21T18:00
  }
}
      
      



. JsonSerializer



, serialize()







null, .





public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (isNull(localDateTime)) {
            return;
        }
        OffsetDateTime timeUtc = localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now()));
        jsonGenerator.writeString(timeUtc.toString());
    }
}
      
      



? ? . , , , UTC+00. , id . ZoneOffset







, UTC+03, : 2021-02-21T18:00+03:00.



UTC+00, 2021-02-21T18:00Z







Como estamos trabalhando com uma string, não será difícil para nós mudar um pouco o código para que sempre tenhamos a data no mesmo formato na saída. Vamos declarar duas constantes - uma delas será igual ao id padrão UTC + 00, e a segunda - que queremos dar ao cliente, e adicionar uma verificação - se o horário do servidor estiver no fuso horário zero, então irá substituí-lo Z



por +00:00



. Como resultado, nosso serializador se parecerá com este





public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

    private static final String UTC_0_OFFSET_ID = "Z";
    private static final String UTC_0_TIMEZONE = "+00:00";

    @Override
    public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        if (!isNull(localDateTime)) {
            String date;
            OffsetDateTime timeUtc = localDateTime.atOffset(ZoneOffset.systemDefault().getRules().getOffset(LocalDateTime.now()));
            if (UTC_0_OFFSET_ID.equals(timeUtc.getOffset().getId())) {
                date = timeUtc.toString().replace(UTC_0_OFFSET_ID, UTC_0_TIMEZONE);
            } else {
                date = timeUtc.toString();
            }
            jsonGenerator.writeString(date);
        }
    }
}
      
      



Total

Graças aos mecanismos integrados de Spring, fomos capazes de converter automaticamente a data e hora no formato necessário, sem nenhuma chamada de método explícita no código





O código-fonte completo pode ser visto aqui








All Articles