wake-up-neo.net

Datumsformat Zuordnung zu JSON Jackson

Ich habe ein Datumsformat von API wie folgt:

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

Welches ist JJJJ-TT-MM HH: MM am/pm GMT Zeitstempel. Ich ordne diesen Wert einer Datumsvariablen in POJO zu. Offensichtlich zeigt es Konvertierungsfehler.

Ich möchte zwei Dinge wissen:

  1. Welche Formatierung muss ich verwenden, um die Konvertierung mit Jackson durchzuführen? Ist Datum ein guter Feldtyp dafür?
  2. Gibt es im Allgemeinen eine Möglichkeit, die Variablen zu verarbeiten, bevor sie von Jackson Object-Mitgliedern zugeordnet werden? So etwas wie das Ändern des Formats, Berechnungen usw.
124
Kevin Rave

Welche Formatierung muss ich verwenden, um die Konvertierung mit Jackson durchzuführen? Ist Datum ein guter Feldtyp dafür?

Date ist dafür ein feiner Feldtyp. Mit ObjectMapper.setDateFormat Können Sie die JSON-Analyse ziemlich einfach durchführen:

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

Gibt es im Allgemeinen eine Möglichkeit, die Variablen zu verarbeiten, bevor sie von Jackson Object-Mitgliedern zugeordnet werden? So etwas wie das Ändern des Formats, Berechnungen usw.

Ja. Sie haben einige Optionen, einschließlich der Implementierung eines benutzerdefinierten JsonDeserializer, z. Erweiterung von JsonDeserializer<Date>. This ist ein guter Anfang.

104
pb2q

Seit Jackson v2.0 können Sie die Annotation @JsonFormat direkt für Object-Mitglieder verwenden.

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
private Date date;
288

Natürlich gibt es einen automatisierten Weg , der als Serialisierung und Deserialisierung bezeichnet wird, und Sie können ihn mit spezifischen Annotationen definieren ( @ JsonSerialize , @ JsonDeserialize ) wie auch von pb2q erwähnt.

Sie können sowohl Java.util.Date als auch Java.util.Calendar ... und wahrscheinlich auch JodaTime verwenden.

Die @ JsonFormat-Annotationen haben bei mir nicht wie gewünscht funktioniert (es hat die Zeitzone angepasst auf einen anderen Wert) während der Deserialisierung (die Serialisierung hat einwandfrei funktioniert):

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

Sie müssen einen benutzerdefinierten Serializer und einen benutzerdefinierten Deserializer anstelle der Anmerkung @JsonFormat verwenden, wenn Sie ein vorhergesagtes Ergebnis wünschen. Ich habe hier ein wirklich gutes Tutorial und eine gute Lösung gefunden. http://www.baeldung.com/jackson-serialize-dates

Es gibt Beispiele für Datumsfelder , aber ich brauchte für Kalenderfelder , damit hier ist meine Implementierung :

Die Serializer Klasse:

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

Die Deserializer Klasse:

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

und die Verwendung der oben genannten Klassen:

public class CalendarEntry {

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

    // ... additional things ...
}

Mit dieser Implementierung wird bei der Ausführung des Serialisierungs- und Deserialisierungsprozesses nacheinander der Origin-Wert ermittelt.

Nur mit der Annotation @JsonFormat ergibt die Deserialisierung ein anderes Ergebnis Ich denke aufgrund der bibliotheksinternen Zeitzonen-Standardeinstellung, was man mit Annotationsparametern nicht ändern kann (das war meine Erfahrung mit Jackson Library 2.5.3 und 2.6.3 Version auch).

45
Miklos Krivan

Nur ein vollständiges Beispiel für Spring Boot Anwendung mit RFC3339 Datum/Uhrzeit-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"));
    }
}
3
BaiJiFeiLong

Aufbauend auf der sehr hilfreichen Antwort von @ miklov-kriven hoffe ich, dass sich diese beiden zusätzlichen Überlegungen für jemanden als hilfreich erweisen:

(1) Ich finde es eine gute Idee, Serializer und De-Serializer als statische innere Klassen in dieselbe Klasse aufzunehmen. Hinweis: Verwenden von ThreadLocal für die Threadsicherheit von 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) Alternativ zur Verwendung von @JsonSerialize- und @JsonDeserialize-Annotationen für jedes einzelne Klassenmitglied können Sie auch erwägen, Jacksons Standardserialisierung zu überschreiben, indem Sie die benutzerdefinierte Serialisierung auf Anwendungsebene anwenden, dh alle Klassenmitglieder vom Typ Datum werden von Jackson serialisiert Verwenden dieser benutzerdefinierten Serialisierung ohne explizite Annotation für jedes Feld. Wenn Sie zum Beispiel Spring Boot verwenden, sieht dies folgendermaßen aus:

@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
Stuart

Wenn jemand Probleme mit der Verwendung eines benutzerdefinierten Datumsformats für Java.sql.Date hat, ist dies die einfachste Lösung:

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

(Diese SO-Antwort hat mir viel Ärger erspart: https://stackoverflow.com/a/35212795/3149048 )

Jackson verwendet standardmäßig den SqlDateSerializer für Java.sql.Date. Derzeit berücksichtigt dieser Serializer jedoch nicht das Datumsformat. Siehe hierzu das folgende Problem: https://github.com/FasterXML/jackson-databind/issues/1407 . Die Problemumgehung besteht darin, einen anderen Serializer für Java.sql.Date zu registrieren, wie im Codebeispiel gezeigt.

1
Stephanie

Ich arbeite für mich. SpringBoot.

 import com.alibaba.fastjson.annotation.JSONField;

 @JSONField(format = "yyyy-MM-dd HH:mm:ss")  
 private Date createTime;

ausgabe:

{ 
   "createTime": "2019-06-14 13:07:21"
}
1
anson

Ich möchte darauf hinweisen, dass das Setzen eines SimpleDateFormat wie in der anderen Antwort beschrieben nur für einen Java.util.Date Funktioniert, von dem ich annehme, dass er in der Frage gemeint ist. Aber für Java.sql.Date Funktioniert der Formatierer nicht. In meinem Fall war es nicht sehr offensichtlich, warum der Formatierer nicht funktionierte, da in dem Modell, das serialisiert werden sollte, das Feld tatsächlich ein Java.utl.Date War, das tatsächliche Objekt jedoch ein Java.sql.Date War. Das ist möglich, weil

public class Java.sql extends Java.util.Date

Das ist also tatsächlich gültig

Java.util.Date date = new Java.sql.Date(1542381115815L);

Wenn Sie sich fragen, warum Ihr Datumsfeld nicht korrekt formatiert ist, stellen Sie sicher, dass das Objekt wirklich ein Java.util.Date Ist.

Hier wird auch erwähnt, warum die Behandlung Java.sql.Date Nicht hinzugefügt wird.

Dies wäre dann ein Umbruch, und ich glaube nicht, dass dies gerechtfertigt ist. Wenn wir von vorne anfangen würden, würde ich der Änderung zustimmen, aber da sind die Dinge nicht so sehr.

1
LazerBass