Mapeamento do formato da data para JSON Jackson

Tenho um formato de data vindo da API assim:

"start_time": "2015-10-1 3:00 PM GMT+1:00"

que é AAAA-DD-MM HH: MM am / pm GMT timestamp. Estou mapeando este valor para uma variável de data em POJO. Obviamente, está mostrando erro de conversão.

Gostaria de saber duas coisas:
    Qual é a formatação que preciso de usar para fazer a conversão com o Jackson? Data é um bom tipo de campo para isso? Em geral, há uma maneira de processar as variáveis antes de serem mapeadas para membros de objetos por Jackson? Algo como, mudar o formato, cálculos, etc.
Author: buræquete, 2012-09-17

6 answers

Qual é a formatação que preciso de usar para fazer a conversão com o Jackson? Data é um bom tipo de campo para isso?

Date é um bom tipo de campo para isso. Você pode fazer o JSON parse-capaz muito facilmente usando ObjectMapper.setDateFormat:

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);
Em geral, há uma forma de processar as variáveis antes de serem mapeadas para membros de objectos pelo Jackson? Algo como, mudar o formato, cálculos, etc.
Sim. Você tem algumas opções, incluindo aplicar um costume JsonDeserializer, por exemplo, estender JsonDeserializer<Date>. Isto é um bom começo.
 82
Author: pb2q, 2012-09-17 19:40:07

Desde o Jackson v2. 0, você pode usar a anotação @JsonFormat directamente nos membros do objecto;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
private Date date;
 233
Author: Olivier Lecrivain, 2017-09-15 04:08:13

, claro, não é uma forma automática chamado de serialização e desserialização e você pode defini-lo com anotações específicas (@JsonSerialize,@JsonDeserialize), como referido por pb2q bem.

Podes usar ambos os java.util.Data e java.util.Calendario ... e provavelmente a mim também.

As anotações @JsonFormat não funcionaram para mim como eu queria {[[7]} (ele tem ajustado o fuso-horário para um valor diferente) durante deserialização (a serialização funcionou perfeitamente):

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "CET")

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Europe/Budapest")

Precisa de usar o serializador personalizado e o deserializador personalizado em vez da anotação @JsonFormat se quiser o resultado previsto. encontrei um tutorial e uma solução muito boas aqui http://www.baeldung.com/jackson-serialize-dates

Existem exemplos paraData campos mas eu precisava para Calendário campos por isso aqui está a minha implementação:

O serializador classe:

public class CustomCalendarSerializer extends JsonSerializer<Calendar> {

    public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    public static final Locale LOCALE_HUNGARIAN = new Locale("hu", "HU");
    public static final TimeZone LOCAL_TIME_ZONE = TimeZone.getTimeZone("Europe/Budapest");

    @Override
    public void serialize(Calendar value, JsonGenerator gen, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
        if (value == null) {
            gen.writeNull();
        } else {
            gen.writeString(FORMATTER.format(value.getTime()));
        }
    }
}

A classedeserializadora :

public class CustomCalendarDeserializer extends JsonDeserializer<Calendar> {

    @Override
    public Calendar deserialize(JsonParser jsonparser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        String dateAsString = jsonparser.getText();
        try {
            Date date = CustomCalendarSerializer.FORMATTER.parse(dateAsString);
            Calendar calendar = Calendar.getInstance(
                CustomCalendarSerializer.LOCAL_TIME_ZONE, 
                CustomCalendarSerializer.LOCALE_HUNGARIAN
            );
            calendar.setTime(date);
            return calendar;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

E o uso das classes acima:

public class CalendarEntry {

    @JsonSerialize(using = CustomCalendarSerializer.class)
    @JsonDeserialize(using = CustomCalendarDeserializer.class)
    private Calendar calendar;

    // ... additional things ...
}

Usando esta implementação a execução do processo de serialização e deserialização resulta consecutivamente o valor de origem.

Só usando a anotação @JsonFormat a deserialização dá resultados diferentes Eu acho que por causa da configuração por omissão do fuso-horário interno da biblioteca o que você não pode alterar com a anotação parameters (that was my experience with Jackson library 2.5.3 and 2.6.3 version as well).

 42
Author: Miklos Krivan, 2015-12-12 08:00:06
Com base na resposta muito útil de @miklov-kriven, espero que estes dois pontos de consideração adicionais sejam úteis a alguém:

(1) acho uma boa ideia incluir o serializador e o Des-serializador como classes internas estáticas na mesma classe. NB: utilizar o ThreadLocal para a segurança do rosca de Format Simpledat.

public class DateConverter {

    private static final ThreadLocal<SimpleDateFormat> sdf = 
        ThreadLocal.<SimpleDateFormat>withInitial(
                () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");});

    public static class Serialize extends JsonSerializer<Date> {
        @Override
        public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception {
            if (value == null) {
                jgen.writeNull();
            }
            else {
                jgen.writeString(sdf.get().format(value));
            }
        }
    }

    public static class Deserialize extends JsonDeserializer<Date> {
        @Overrride
        public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception {
            String dateAsString = jp.getText();
            try {
                if (Strings.isNullOrEmpty(dateAsString)) {
                    return null;
                }
                else {
                    return new Date(sdf.get().parse(dateAsString).getTime());
                }
            }
            catch (ParseException pe) {
                throw new RuntimeException(pe);
            }
        }
    }
}

(2) como uma alternativa ao uso de anotações @JsonSerialize e @JsonDeserialize em cada membro individual da classe, Você também pode considerar a hipótese de anular as anotações de Jackson a serialização padrão aplicando a serialização personalizada em um nível de aplicação, ou seja, todos os membros da classe da data do tipo serão serializados por Jackson usando esta serialização personalizada sem anotação explícita em cada campo. Se estiver a usar o Spring Boot, por exemplo, uma forma de o fazer seria a seguinte:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public Module customModule() {
        SimpleModule module = new SimpleModule();
        module.addSerializer(Date.class, new DateConverter.Serialize());
        module.addDeserializer(Date.class, new Dateconverter.Deserialize());
        return module;
    }
}
 1
Author: Stuart, 2016-05-09 11:31:43

Se alguém tiver problemas em usar um formato de dados personalizado para java.banco.Data, esta é a solução mais simples:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(java.sql.Date.class, new DateSerializer());
mapper.registerModule(module);
Esta resposta salvou - me muitos problemas. https://stackoverflow.com/a/35212795/3149048 )

O Jackson usa o Sqldateserializador por omissão para java.banco.Data, mas de momento, este serializador não leva em conta o formato de dados; veja este número: https://github.com/FasterXML/jackson-databind/issues/1407 . A solução é: registe um serializador diferente para java.banco.Data como mostrado no exemplo de código.

 1
Author: Stephanie, 2017-06-15 07:27:17

Apenas um exemplo completo para spring boot aplicação com RFC3339 datetime format

package bj.demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;

import java.text.SimpleDateFormat;

/**
 * Created by [email protected] at 2018/5/4 10:22
 */
@SpringBootApplication
public class BarApp implements ApplicationListener<ApplicationReadyEvent> {

    public static void main(String[] args) {
        SpringApplication.run(BarApp.class, args);
    }

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
    }
}
 0
Author: BaiJiFeiLong, 2018-05-04 11:35:50