Mappage de format de Date vers JSON Jackson

J'ai un format de Date provenant de L'API comme ceci:

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

Qui est AAAA-JJ-MM HH: MM am / pm GMT timestamp. Je mappe cette valeur à une variable de Date dans POJO. Évidemment, son erreur de conversion montrant.

Je voudrais savoir 2 choses:

  1. Quel est le formatage que je dois utiliser pour effectuer la conversion avec Jackson? La Date est-elle un bon type de champ pour cela?
  2. en général, existe-t-il un moyen de traiter les variables avant qu'elles ne soient mappées aux membres de L'objet par Jackson? Quelque chose comme, changer le format, les calculs, etc.
96
demandé sur buræquete 2012-09-17 20:03:01

6 réponses

Quel est le formatage que je dois utiliser pour effectuer la conversion avec Jackson? La Date est-elle un bon type de champ pour cela?

Date est un type de champ fin pour cela. Vous pouvez rendre L'analyse JSON assez facilement en utilisant ObjectMapper.setDateFormat:

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);

En général, existe-t-il un moyen de traiter les variables avant Qu'elles ne soient mappées aux membres D'objet par Jackson? Quelque chose comme, changer le format, les calculs, etc.

Oui. Vous avez quelques options, y compris la mise en œuvre un JsonDeserializer personnalisé, par exemple en étendant JsonDeserializer<Date>. Ce est un bon début.

82
répondu pb2q 2012-09-17 19:40:07

Depuis Jackson v2. 0, Vous pouvez utiliser l'annotation @JsonFormat directement sur les membres de L'objet;

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

Bien sûr Il existe un moyen automatisé appelé sérialisation et désérialisation et vous pouvez le définir avec des annotations spécifiques (@JsonSerialize,@JsonDeserialize ) comme mentionné par pb2q aussi.

Vous pouvez utiliser les deux java.util.Date et java.util.Calendrier ... et probablement JodaTime aussi.

Les annotations @JsonFormat n'ont pas fonctionné pour moi comme je le voulais (il a ajusté le fuseau horaire à une valeur différente) pendant désérialisation (la sérialisation a fonctionné parfaitement):

@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")

Vous devez utiliser un sérialiseur personnalisé et un désérialiseur personnalisé au lieu de l'annotation @JsonFormat si vous voulez un résultat prédit. j'ai trouvé un très bon tutoriel et une solution ici http://www.baeldung.com/jackson-serialize-dates

, Il existe des exemples de Date champs, mais j'avais besoin pour Agenda champs voici mon implémentation:

Le sérialiseur 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()));
        }
    }
}

La classe désérialiseur :

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

Et l'utilisation de des classes ci-dessus:

public class CalendarEntry {

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

    // ... additional things ...
}

En utilisant cette implémentation, l'exécution du processus de sérialisation et de désérialisation résulte consécutivement de la valeur d'origine.

En utilisant uniquement l'annotation @JsonFormat, la désérialisation donne un résultat différent je pense qu'à cause de la configuration par défaut du fuseau horaire interne de la bibliothèque, ce que vous ne pouvez pas changer avec l'annotation paramètres (c'était mon expérience avec la version 2.5.3 et 2.6.3 de la bibliothèque Jackson).

42
répondu Miklos Krivan 2015-12-12 08:00:06

S'appuyant sur la réponse très utile de @miklov-kriven, j'espère que ces deux points supplémentaires de considération s'avéreront utiles à quelqu'un:

(1) je trouve une bonne idée d'inclure serializer et de-serializer en tant que classes internes statiques dans la même classe. NB, en utilisant ThreadLocal pour la sécurité des threads de SimpleDateFormat.

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) comme alternative à l'utilisation des annotations @JsonSerialize et @jsondeserialize sur chaque membre de la classe, vous pouvez également envisager de remplacer la valeur par défaut de Jackson sérialisation en appliquant la sérialisation personnalisée au niveau de l'application, C'est-à-dire que tous les membres de la classe de type Date seront sérialisés par Jackson en utilisant cette sérialisation personnalisée sans annotation explicite sur chaque champ. Si vous utilisez Spring Boot par exemple, une façon de le faire serait la suivante:

@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
répondu Stuart 2016-05-09 11:31:43

Si quelqu'un a des problèmes avec l'utilisation d'un dateformat personnalisé pour java.SQL.Date, c'est la solution la plus simple:

ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(java.sql.Date.class, new DateSerializer());
mapper.registerModule(module);

(Cette réponse m'a sauvé beaucoup de mal: https://stackoverflow.com/a/35212795/3149048 )

Jackson utilise le SqlDateSerializer par défaut pour java.SQL.Date, mais actuellement, ce sérialiseur ne prend pas en compte le dateformat, voir ce problème: https://github.com/FasterXML/jackson-databind/issues/1407 . La solution de contournement consiste à enregistrer un sérialiseur différent pour java.SQL.Date comme indiqué dans l'exemple de code.

1
répondu Stephanie 2017-06-15 07:27:17

Juste un exemple complet pour spring boot application avec RFC3339 format datetime

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 BaiJiFeiLong@gmail.com 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
répondu BaiJiFeiLong 2018-05-04 11:35:50