Datumsformat Zuordnung zu JSON Jackson

Ich habe ein Datumsformat, das von API wie folgt kommt:

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

Welches ist JJJJ-TT-MM HH: MM am / pm GMT Zeitstempel. Ich mappe diesen Wert auf eine Date-Variable in POJO. Offensichtlich zeigt es Konvertierungserrors.

Ich möchte 2 Dinge wissen:

  1. Welche Formatierung benötige ich, um eine Konvertierung mit Jackson durchzuführen? Ist Date ein guter Feldtyp dafür?
  2. Gibt es im Allgemeinen eine Möglichkeit, die Variablen zu verarbeiten, bevor sie von Jackson auf Objektmitglieder abgebildet werden? Ähnliches, Ändern des Formats, der Berechnungen usw.

Welche Formatierung benötige ich, um eine Konvertierung mit Jackson durchzuführen? Ist Date ein guter Feldtyp dafür?

Date ist ein feiner Feldtyp dafür. Sie können die JSON-Analyse sehr einfach machen, indem Sie ObjectMapper.setDateFormat :

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

Gibt es im Allgemeinen eine Möglichkeit, die Variablen zu verarbeiten, bevor sie von Jackson auf Objektmitglieder abgebildet werden? Ähnliches, Ändern des Formats, der Berechnungen usw.

Ja. Sie haben einige Optionen, einschließlich der Implementierung eines benutzerdefinierten JsonDeserializer , z. B. das Erweitern von JsonDeserializer . Das ist ein guter Anfang.

Seit Jackson v2.0 können Sie @ JsonFormat Annotation direkt auf Objekt-Mitgliedern verwenden;

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

Natürlich gibt es einen automatisierten Weg namens Serialisierung und Deserialisierung und Sie können ihn mit spezifischen Annotationen ( @JsonSerialize , @JsonDeserialize ) definieren, 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 funktionierten bei der Deserialisierung (die Serialisierung funktionierte perfekt) nicht für mich (ich habe die Zeitzone auf einen anderen Wert eingestellt ):

 @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 @ JsonFormat-Annotation verwenden, wenn Sie ein vorhergesagtes Ergebnis wünschen. Ich habe wirklich gutes Tutorial und Lösung http://www.baeldung.com/jackson-serialize-dates gefunden

Es gibt Beispiele für Datumsfelder , die ich jedoch für Kalenderfelder benötigte. Hier ist meine Implementierung :

Die Serialisierungsklasse :

 public class CustomCalendarSerializer extends JsonSerializer { 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- class:

 public class CustomCalendarDeserializer extends JsonDeserializer { @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 classn:

 public class CalendarEntry { @JsonSerialize(using = CustomCalendarSerializer.class) @JsonDeserialize(using = CustomCalendarDeserializer.class) private Calendar calendar; // ... additional things ... } 

Unter Verwendung dieser Implementierung ergibt die Ausführung des Serialisierungs- und Deserialisierungsprozesses nacheinander den Ursprungswert.

Nur mit der @ JsonFormat-Annotation ergibt die Deserialisierung ein anderes Ergebnis, denke ich aufgrund der internen Zeitzonen-Standard-Einrichtung der Bibliothek, was Sie nicht mit Annotation-Parametern ändern können (das war meine Erfahrung mit Jackson-Bibliothek 2.5.3 und 2.6.3-Version).

Aufbauend auf der sehr hilfreichen Antwort von @ miklov-kriven hoffe ich, dass diese beiden zusätzlichen Punkte der Überlegung für jemanden hilfreich sind:

(1) Ich finde es eine gute Idee, Serializer und De-Serializer als statische innere classn in derselben class zu integrieren. Hinweis: Verwenden Sie ThreadLocal für die Thread-Sicherheit von SimpleDateFormat.

 public class DateConverter { private static final ThreadLocal sdf = ThreadLocal.withInitial( () -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm az");}); public static class Serialize extends JsonSerializer { @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 { @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) Als Alternative zur Verwendung von @ JsonSerialize- und @ JsonDeserialize-Annotationen für jedes einzelne classnmitglied könnten Sie auch Jacksons Standardserialisierung überschreiben, indem Sie die benutzerdefinierte Serialisierung auf Anwendungsebene anwenden, dh alle classnmitglieder vom Typ Date werden von Jackson serialisiert Verwenden Sie diese benutzerdefinierte Serialisierung ohne explizite Anmerkung für jedes Feld. Wenn Sie zum Beispiel Spring Boot verwenden, würde dies wie folgt aussehen:

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

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 eine Menge Ärger erspart: https://stackoverflow.com/a/35212795/3149048 )

Jackson verwendet den SqlDateSerializer standardmäßig für java.sql.Date, aber derzeit berücksichtigt dieser Serializer das Datumsformat nicht, siehe dieses 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.

Nur ein vollständiges Beispiel für eine Spring-Boot- Anwendung mit dem 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 BaiJiFeiLong@gmail.com at 2018/5/4 10:22 */ @SpringBootApplication public class BarApp implements ApplicationListener { 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")); } }