Jackson Date

1. Oversikt

I denne opplæringen serier vi datoer med Jackson. Vi starter med å serieisere en enkel java.util.Dato, deretter Joda-Time samt Java 8 Dato tid.

2. Serialiser Dato til tidsstempel

Først - la oss se hvordan vi serierer en enkel java.util.Date med Jackson.

I det følgende eksemplet - serier vi en forekomst av “Begivenhet”Som har en Dato felt "hendelsesdato“:

@Test offentlig ugyldig nårSerializingDateWithJackson_thenSerializedToTimestamp () kaster JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-åååå hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); Dato dato = df.parse ("01-01-1970 01:00"); Event event = new Event ("party", date); ObjectMapper mapper = ny ObjectMapper (); mapper.writeValueAsString (hendelse); }

Det som er viktig her er at Jackson serierer datoen til et tidsstempelformat som standard (antall millisekunder siden 1. januar 1970, UTC).

Den faktiske produksjonen avbegivenhet”Serialisering er:

{"name": "party", "eventDate": 3600000}

3. Serialiser Dato til ISO-8601

Serialisering til dette korte tidsstempelformatet er ikke optimalt. La oss nå serieisere Dato til ISO-8601 format:

@Test offentlig ugyldig nårSerializingDateToISO8601_thenSerializedToText () kaster JsonProcessingException, ParseException {SimpleDateFormat df = ny SimpleDateFormat ("dd-MM-åååå hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); String toParse = "01-01-1970 02:30"; Dato dato = df.parse (toParse); Event event = new Event ("party", date); ObjectMapper mapper = ny ObjectMapper (); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // StdDateFormat er ISO8601 siden jackson 2.9 mapper.setDateFormat (ny StdDateFormat (). MedColonInTimeZone (true)); Strengresultat = mapper.writeValueAsString (hendelse); assertThat (resultat, inneholderString ("1970-01-01T02: 30: 00.000 + 00: 00")); }

Legg merke til hvordan representasjonen av datoen nå er mye mer lesbar.

4. Konfigurer ObjectMapperDateFormat

De tidligere løsningene mangler fortsatt full fleksibilitet til å velge det nøyaktige formatet som skal representere java.util.Date tilfeller.

La oss nå ta en titt på en konfigurasjon som lar oss gjøre det angi formatene våre for å representere datoer:

@Test offentlig ugyldig nårSettingObjectMapperDateFormat_thenCorrect () kaster JsonProcessingException, ParseException {SimpleDateFormat df = ny SimpleDateFormat ("dd-MM-åååå hh: mm"); String toParse = "20-12-2014 02:30"; Dato dato = df.parse (toParse); Event event = new Event ("party", date); ObjectMapper mapper = ny ObjectMapper (); mapper.setDateFormat (df); String result = mapper.writeValueAsString (event); assertThat (resultat, inneholderString (toParse)); }

Merk at selv om vi nå er mer fleksible når det gjelder datoformat, bruker vi fremdeles en global konfigurasjon på nivået av hele ObjectMapper.

5. Bruk @JsonFormat til Format Dato

La oss ta en titt på @JsonFormat kommentar til kontrollere datoformatet på individuelle klasser i stedet for globalt, for hele applikasjonen:

public class Event {public String name; @JsonFormat (form = JsonFormat.Shape.STRING, mønster = "dd-MM-åååå tt: mm: ss") offentlig Dato eventDate; }

Nå - la oss teste det:

@Test offentlig ugyldig nårUsingJsonFormatAnnotationToFormatDate_thenCorrect () kaster JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-åååå hh: mm: ss"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); String toParse = "20-12-2014 02:30:00"; Dato dato = df.parse (toParse); Event event = new Event ("party", date); ObjectMapper mapper = ny ObjectMapper (); String result = mapper.writeValueAsString (event); assertThat (resultat, inneholderString (toParse)); }

6. Tilpasset Dato Serializer

Neste - for å få full kontroll over utdataene, bruker vi en tilpasset serializer for datoer:

offentlig klasse CustomDateSerializer utvider StdSerializer {private SimpleDateFormat formatter = ny SimpleDateFormat ("dd-MM-åååå hh: mm: ss"); offentlig CustomDateSerializer () {dette (null); } offentlig CustomDateSerializer (klasse t) {super (t); } @ Override public void serialize (Date value, JsonGenerator gen, SerializerProvider arg2) kaster IOException, JsonProcessingException {gen.writeString (formatter.format (verdi)); }}

Neste - la oss bruke den som serielliseringsenhet til vår “hendelsesdato" felt:

public class Event {public String name; @JsonSerialize (bruker = CustomDateSerializer.class) offentlig dato eventDate; }

Til slutt - la oss teste det:

@Test offentlig ugyldig nårUsingCustomDateSerializer_thenCorrect () kaster JsonProcessingException, ParseException {SimpleDateFormat df = ny SimpleDateFormat ("dd-MM-åååå hh: mm: ss"); String toParse = "20-12-2014 02:30:00"; Dato dato = df.parse (toParse); Event event = new Event ("party", date); ObjectMapper mapper = ny ObjectMapper (); String result = mapper.writeValueAsString (event); assertThat (resultat, inneholderString (toParse)); }

7. Serialiser Joda-Time With Jackson

Datoer er ikke alltid en forekomst av java.util.Date; faktisk - de er mer og mer representert av noen annen klasse - og en vanlig er selvfølgelig Dato tid implementering fra Joda-Time-biblioteket.

La oss se hvordan vi kan serialisere Dato tid med Jackson.

Vi vil bruke jackson-datatype-joda modul for out-of-the-box Joda-Time støtte:

 com.fasterxml.jackson.datatype jackson-datatype-joda 2.9.7 

Og nå kan vi bare registrere JodaModule og gjør deg:

@Test offentlig ugyldig nårSerializingJodaTime_thenCorrect () kaster JsonProcessingException {DateTime date = new DateTime (2014, 12, 20, 2, 30, DateTimeZone.forID ("Europe / London")); ObjectMapper mapper = ny ObjectMapper (); mapper.registerModule (ny JodaModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Strengresultat = mapper.writeValueAsString (dato); assertThat (resultat, inneholderString ("2014-12-20T02: 30: 00.000Z")); }

8. Serier Joda Dato tid Med Custom Serializer

Hvis vi ikke vil ha den ekstra Joda-Time Jackson-avhengigheten - kan vi også gjøre bruk av det en tilpasset serielliser (ligner på de tidligere eksemplene) å få Dato tid tilfeller serialisert rent:

offentlig klasse CustomDateTimeSerializer utvider StdSerializer {privat statisk DateTimeFormatter formatter = DateTimeFormat.forPattern ("åååå-MM-dd HH: mm"); offentlig CustomDateTimeSerializer () {dette (null); } offentlig CustomDateTimeSerializer (klasse t) {super (t); } @Override public void serialize (DateTime value, JsonGenerator gen, SerializerProvider arg2) kaster IOException, JsonProcessingException {gen.writeString (formatter.print (verdi)); }}

Neste - la oss bruke det som vår eiendom “hendelsesdato”Serializer:

public class Event {public String name; @JsonSerialize (bruker = CustomDateTimeSerializer.class) offentlig DateTime eventDate; }

Til slutt - la oss sette alt sammen og teste det:

@Test offentlig ugyldig nårSerializingJodaTimeWithJackson_thenCorrect () kaster JsonProcessingException {DateTime date = new DateTime (2014, 12, 20, 2, 30); Event event = new Event ("party", date); ObjectMapper mapper = ny ObjectMapper (); Strengresultat = mapper.writeValueAsString (hendelse); assertThat (resultat, inneholderString ("2014-12-20 02:30")); }

9. Serialiser Java 8 Dato Med Jackson

Neste - la oss se hvordan vi serialiserer Java 8 Dato tid - i dette eksemplet, LocalDateTime - bruker Jackson. Vi kan benytte oss av jackson-datatype-jsr310 modul:

 com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.9.7 

Nå er alt vi trenger å registrere JavaTimeModule (JSR310Modul er utfaset) og Jackson vil ta seg av resten:

@Test offentlig ugyldig nårSerializingJava8Date_thenCorrect () kaster JsonProcessingException {LocalDateTime date = LocalDateTime.of (2014, 12, 20, 2, 30); ObjectMapper mapper = ny ObjectMapper (); mapper.registerModule (ny JavaTimeModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Strengresultat = mapper.writeValueAsString (dato); assertThat (resultat, inneholderString ("2014-12-20T02: 30")); }

10. Serialiser Java 8 Dato Uten ekstra avhengighet

Hvis du ikke vil ha den ekstra avhengigheten, kan du alltid bruke den en tilpasset serializer for å skrive ut Java 8 Dato tid til JSON:

offentlig klasse CustomLocalDateTimeSerializer utvider StdSerializer {privat statisk DateTimeFormatter formatter = DateTimeFormatter.ofPattern ("åååå-MM-dd HH: mm"); offentlig CustomLocalDateTimeSerializer () {dette (null); } offentlig CustomLocalDateTimeSerializer (klasse t) {super (t); } @Override public void serialize (LocalDateTime value, JsonGenerator gen, SerializerProvider arg2) kaster IOException, JsonProcessingException {gen.writeString (formatter.format (verdi)); }}

Neste - la oss bruke serialisereren til vår “hendelsesdato" felt:

public class Event {public String name; @JsonSerialize (bruker = CustomLocalDateTimeSerializer.class) offentlig LocalDateTime eventDate; }

Nå - la oss teste det:

@Test offentlig ugyldig nårSerializingJava8DateWithCustomSerializer_thenCorrect () kaster JsonProcessingException {LocalDateTime date = LocalDateTime.of (2014, 12, 20, 2, 30); Event event = new Event ("party", date); ObjectMapper mapper = ny ObjectMapper (); String result = mapper.writeValueAsString (event); assertThat (resultat, inneholderString ("2014-12-20 02:30")); }

11. Deserialiser Dato

Neste - la oss se hvordan du deserialiserer en Dato med Jackson. I det følgende eksemplet - deserialiserer vi en “Begivenhet”Forekomst som inneholder en dato:

@Test offentlig ugyldig nårDeserializingDateWithJackson_thenCorrect () kaster JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 20-12-2014 02:30:00 "}"; SimpleDateFormat df = ny SimpleDateFormat ("dd-MM-åååå hh: mm: ss"); ObjectMapper mapper = ny ObjectMapper (); mapper.setDateFormat (df); Event event = mapper.readerFor (Event.class) .readValue (json); assertEquals ("20-12-2014 02:30:00", df.format (event.eventDate)); }

12. Deserialiser Joda ZonedDateTime Med tidssone bevart

I sin standardkonfigurasjon justerer Jackson tidssonen til en Joda ZonedDateTime til tidssonen til den lokale konteksten. Siden tidssonen til den lokale konteksten som standard ikke er angitt og må konfigureres manuelt, justerer Jackson tidssonen til GMT:

@Test offentlig ugyldig nårDeserialisingZonedDateTimeWithDefaults_thenNotCorrect () kaster IOException {ObjectMapper objectMapper = ny ObjectMapper (); objectMapper.findAndRegisterModules (); objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); ZonedDateTime now = ZonedDateTime.now (ZoneId.of ("Europe / Berlin")); Streng konvertert = objectMapper.writeValueAsString (nå); ZonedDateTime gjenopprettet = objectMapper.readValue (konvertert, ZonedDateTime.class); System.out.println ("serialisert:" + nå); System.out.println ("gjenopprettet:" + gjenopprettet); assertThat (nå, er (gjenopprettet)); }

Denne testsaken mislykkes med utdata:

seriell: 2017-08-14T13: 52: 22.071 + 02: 00 [Europe / Berlin] restaurert: 2017-08-14T11: 52: 22.071Z [UTC]

Heldigvis er det en rask og enkel løsning for denne rare standardadferden: vi må bare si til Jackson, ikke å justere tidssonen.

Dette kan gjøres ved å legge til nedenstående kodelinje i ovennevnte testtilfelle:

objectMapper.disable (DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

Vær oppmerksom på at for å bevare tidssonen, må vi også deaktivere standardoppførselen til å serialisere datoen til tidsstempelet.

13. Tilpasset Dato Deserializer

La oss også se hvordan du bruker en skikk Dato deserializer; vi skriver en egendefinert deserializer for eiendommen “hendelsesdato“:

offentlig klasse CustomDateDeserializer utvider StdDeserializer {private SimpleDateFormat formatter = nye SimpleDateFormat ("dd-MM-åååå hh: mm: ss"); offentlig CustomDateDeserializer () {dette (null); } offentlig CustomDateDeserializer (klasse vc) {super (vc); } @ Override public Dato deserialize (JsonParser jsonparser, DeserializationContext context) kaster IOException, JsonProcessingException {String date = jsonparser.getText (); prøv {return formatter.parse (dato); } fange (ParseException e) {kaste ny RuntimeException (e); }}}

Neste - la oss bruke det som “hendelsesdato”Deserializer:

public class Event {public String name; @JsonDeserialize (ved hjelp av = CustomDateDeserializer.class) offentlig dato eventDate; }

Og til slutt - la oss teste det:

@Test offentlig ugyldig nårDeserializingDateUsingCustomDeserializer_thenCorrect () kaster JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 20-12-2014 02:30:00 "}"; SimpleDateFormat df = ny SimpleDateFormat ("dd-MM-åååå hh: mm: ss"); ObjectMapper mapper = ny ObjectMapper (); Event event = mapper.readerFor (Event.class) .readValue (json); assertEquals ("20-12-2014 02:30:00", df.format (event.eventDate)); }

14. Fiksing Ugyldig definisjonUnntak

Når du oppretter en LocalDate for eksempel kan vi komme over et unntak:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Kan ikke konstruere forekomst av `java.time.LocalDate '(ingen skapere, som standardkonstruksjon, eksisterer): ingen strengargumentkonstruktør / fabrikkmetode for å deserialisere fra strengverdi (' 2014 -12-20 ') på [Kilde: (String) "2014-12-20"; linje: 1, kolonne: 1]

Dette problemet oppstår fordi JSON ikke har et datoformat, så representerer datoer som String.

De String representasjon av en dato er ikke den samme som et objekt av typen LocalDate i minnet, så vi trenger en ekstern deserializer for å lese det feltet fra en String, og en serializer å gjengi datoen til String format.

Disse metodene gjelder også for LocalDateTime - den eneste endringen er å bruke en tilsvarende klasse for LocalDateTime.

14.1. Jackson Avhengighet

Jackson tillater oss å fikse dette på noen måter. Først må vi sørge for at jsr310 avhengighet er i vår pom.xml:

 com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.11.0 

14.2. Serialisering til enkelt datoobjekt

For å kunne håndtere LocalDate, må vi registrere JavaTimeModule med vår ObjectMapper.

Vi deaktiverer også funksjonen WRITE_DATES_AS_TIMESTAMPS i ObjectMapper for å forhindre at Jackson legger til tidssifre til JSON-utgangen:

@Test offentlig ugyldig når SerializingJava8DateAndReadingValue_thenCorrect () kaster IOException {String stringDate = "\" 2014-12-20 \ ""; ObjectMapper mapper = ny ObjectMapper (); mapper.registerModule (ny JavaTimeModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); LocalDate resultat = mapper.readValue (stringDate, LocalDate.class); assertThat (result.toString (), containString ("2014-12-20")); }

Her har vi brukt Jacksons opprinnelige støtte for serialisering og deserialisering av datoer.

14.3. Kommentar i POJO

En annen måte å håndtere det problemet på er å bruke LocalDateDeserializer og JsonFormat merknader på enhetsnivå:

offentlig klasse EventWithLocalDate {@JsonDeserialize (ved hjelp av = LocalDateDeserializer.class) @JsonSerialize (ved hjelp av = LocalDateSerializer.class) @JsonFormat (form = JsonFormat.Shape.STRING, mønster = "dd-MM-åååå") offentlig LocalDate eventDate; }

De @JsonDeserialize merknader brukes til å spesifisere en egendefinert deserializer for å fjerne markeringen av JSON-objektet. På samme måte, @JsonSerialize angir en egendefinert serielliseringsenhet som skal brukes når du skal organisere enheten.

I tillegg kommentaren @JsonFormat tillater oss å spesifisere formatet som vi skal serieisere datoverdiene til. Derfor kan denne POJO brukes til å lese og skrive JSON:

@Test offentlig ugyldig når SerializingJava8DateAndReadingFromEntity_thenCorrect () kaster IOException {String json = "{\" name \ ": \" party \ ", \" eventDate \ ": \" 20-12-2014 \ "}"; ObjectMapper mapper = ny ObjectMapper (); EventWithLocalDate result = mapper.readValue (json, EventWithLocalDate.class); assertThat (result.getEventDate (). toString (), containString ("2014-12-20")); }

Mens denne tilnærmingen tar mer arbeid enn å bruke JavaTimeModule standardinnstillinger, det er mye mer tilpassbart.

15. Konklusjon

I dette omfattende Dato artikkel, så vi på flere måter Jackson kan hjelpe marskalk og unmarshal en date til JSON ved hjelp av et fornuftig format vi har kontroll over.

Som alltid kan eksemplene koden bli funnet på GitHub.


$config[zx-auto] not found$config[zx-overlay] not found