Jackson Date

1. Pregled

U ovom ćemo uputstvu serijski spojiti datume s Jacksonom. Započet ćemo serializacijom jednostavnog java.util.Datum, zatim Joda-Time kao i Java 8 Datum vrijeme.

2. Serijalizirati Datum u Timestamp

Prvo - hajde da vidimo kako serializirati jednostavan java.util.Datum s Jacksonom.

U sljedećem primjeru - serializirat ćemo instancu „Događaj”Koji ima a Datum polje “datum događaja“:

@Test public void whenSerializingDateWithJackson_thenSerializedToTimestamp () baca JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); Datum datum = df.parse ("01-01-1970 01:00"); Događaj događaja = novi Događaj ("zabava", datum); Mapa ObjectMapper = novi ObjectMapper (); mapper.writeValueAsString (događaj); }

Ono što je ovdje važno jest da će Jackson prema zadanim postavkama serijski označiti datum u format vremenske oznake (broj milisekundi od 1. siječnja 1970. UTC).

Stvarni izlaz "događaj”Serializacija je:

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

3. Serijalizirati Datum prema ISO-8601

Serijalizacija ovog kratkog formata vremenske oznake nije optimalna. Ajmo sada serializirati Datum prema ISO-8601 format:

@Test public void whenSerializingDateToISO8601_thenSerializedToText () baca JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); String toParse = "01-01-1970 02:30"; Datum datum = df.parse (toParse); Događaj događaja = novi Događaj ("zabava", datum); Mapa ObjectMapper = novi ObjectMapper (); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // StdDateFormat je ISO8601 od jackson 2.9 mapper.setDateFormat (novi StdDateFormat (). WithColonInTimeZone (true)); Rezultat niza = mapper.writeValueAsString (događaj); assertThat (rezultat, sadržiString ("1970-01-01T02: 30: 00.000 + 00: 00")); }

Imajte na umu kako je prikaz datuma sada puno čitljiviji.

4. Konfigurirajte ObjectMapperOblik datuma

Prethodnim rješenjima još uvijek nedostaje puna fleksibilnost odabira točnog formata koji će predstavljati java.util.Datum instance.

Pogledajmo sada konfiguraciju koja će nam to omogućiti postavite naše formate za predstavljanje datuma:

@Test public void whenSettingObjectMapperDateFormat_thenCorrect () baca JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm"); String toParse = "20-12-2014 02:30"; Datum datum = df.parse (toParse); Događaj događaja = novi Događaj ("zabava", datum); Mapa ObjectMapper = novi ObjectMapper (); mapper.setDateFormat (df); Rezultat niza = mapper.writeValueAsString (događaj); assertThat (rezultat, sadržiString (toParse)); }

Imajte na umu da, iako smo sada fleksibilniji u pogledu formata datuma - i dalje koristimo globalnu konfiguraciju na razini cjeline ObjectMapper.

5. Koristite @JsonFormat formatirati Datum

Dalje, pogledajmo @JsonFormat bilješka na kontrolirati format datuma na pojedinim predavanjima umjesto globalno, za cijelu aplikaciju:

javni razred Događaj {naziv javnog niza; @JsonFormat (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh: mm: ss") javni datum eventDate; }

Sada - testirajmo:

@Test public void whenUsingJsonFormatAnnotationToFormatDate_thenCorrect () baca JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); String toParse = "20-12-2014 02:30:00"; Datum datum = df.parse (toParse); Događaj događaja = novi Događaj ("zabava", datum); Mapa ObjectMapper = novi ObjectMapper (); Rezultat niza = mapper.writeValueAsString (događaj); assertThat (rezultat, sadržiString (toParse)); }

6. Prilagođeno Datum Serijalizator

Dalje - da bismo dobili potpunu kontrolu nad izlazom, iskoristit ćemo prilagođeni serializator za datume:

javna klasa CustomDateSerializer proširuje StdSerializer {private SimpleDateFormat formatter = novi SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); javni CustomDateSerializer () {this (null); } javni CustomDateSerializer (klasa t) {super (t); } @Override public void serialize (vrijednost datuma, JsonGenerator gen, SerializerProvider arg2) baca IOException, JsonProcessingException {gen.writeString (formatter.format (value)); }}

Dalje - upotrijebimo ga kao serializator našeg "datum događajaPolje:

javni razred Događaj {naziv javnog niza; @JsonSerialize (koristeći = CustomDateSerializer.class) javni datum eventDate; }

Napokon - testirajmo:

@Test public void whenUsingCustomDateSerializer_thenCorrect () baca JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); String toParse = "20-12-2014 02:30:00"; Datum datum = df.parse (toParse); Događaj događaja = novi Događaj ("zabava", datum); Mapa ObjectMapper = novi ObjectMapper (); Rezultat niza = mapper.writeValueAsString (događaj); assertThat (rezultat, sadržiString (toParse)); }

7. Serijalizirajte Joda-Time s Jacksonom

Datumi nisu uvijek primjer java.util.Datum; zapravo - sve ih više predstavlja neka druga klasa - a zajednička je, naravno, Datum vrijeme implementacija iz biblioteke Joda-Time.

Da vidimo kako možemo serializirati Datum vrijeme s Jacksonom.

Iskoristit ćemo jackson-datatype-joda modul za gotovu Joda-Time podršku:

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

A sada možemo jednostavno registrirati JodaModule i biti gotovo:

@Test public void whenSerializingJodaTime_thenCorrect () baca JsonProcessingException {DateTime date = new DateTime (2014, 12, 20, 2, 30, DateTimeZone.forID ("Europe / London")); Mapa ObjectMapper = novi ObjectMapper (); mapper.registerModule (novi JodaModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Rezultat niza = mapper.writeValueAsString (datum); assertThat (rezultat, sadržiString ("2014-12-20T02: 30: 00.000Z")); }

8. Serijalizirajte Jodu Datum vrijeme S prilagođenim serializatorom

Ako ne želimo dodatnu ovisnost o Joda-Time Jacksonu - također možemo iskoristiti prilagođeni serializator (slično ranijim primjerima) da biste dobili Datum vrijeme primjerci čisto serializirani:

javna klasa CustomDateTimeSerializer proširuje StdSerializer {private static DateTimeFormatter formatter = DateTimeFormat.forPattern ("yyyy-MM-dd HH: mm"); javni CustomDateTimeSerializer () {this (null); } javni CustomDateTimeSerializer (klasa t) {super (t); } @Override public void serialize (DateTime value, JsonGenerator gen, SerializerProvider arg2) baca IOException, JsonProcessingException {gen.writeString (formatter.print (value)); }}

Dalje - iskoristimo ga kao svoje vlasništvo “datum događaja”Serializator:

javni razred Događaj {naziv javnog niza; @JsonSerialize (pomoću = CustomDateTimeSerializer.class) javni DateTime eventDate; }

Napokon - spojimo sve i isprobajmo:

@Test public void whenSerializingJodaTimeWithJackson_thenCorrect () baca JsonProcessingException {DateTime date = new DateTime (2014, 12, 20, 2, 30); Događaj događaja = novi Događaj ("zabava", datum); Mapa ObjectMapper = novi ObjectMapper (); Rezultat niza = mapper.writeValueAsString (događaj); assertThat (rezultat, sadržiString ("2014-12-20 02:30")); }

9. Serijalizirajte Java 8 Datum S Jacksonom

Dalje - pogledajmo kako serializirati Javu 8 Datum vrijeme - u ovom primjeru, LocalDateTime - koristeći Jacksona. Možemo se poslužiti jackson-datatype-jsr310 modul:

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

Sada, sve što trebamo učiniti je registrirati JavaTimeModule (JSR310Modul zastarjelo), a Jackson će se pobrinuti za ostalo:

@Test public void whenSerializingJava8Date_thenCorrect () baca JsonProcessingException {LocalDateTime date = LocalDateTime.of (2014, 12, 20, 2, 30); Mapa ObjectMapper = novi ObjectMapper (); mapper.registerModule (novi JavaTimeModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Rezultat niza = mapper.writeValueAsString (datum); assertThat (rezultat, sadržiString ("2014-12-20T02: 30")); }

10. Serijalizirajte Java 8 Datum Bez ikakve dodatne ovisnosti

Ako ne želite dodatnu ovisnost, uvijek možete koristiti prilagođeni serializator za ispis Java 8 Datum vrijeme u JSON:

javna klasa CustomLocalDateTimeSerializer proširuje StdSerializer {privatni statički DateTimeFormatter formatter = DateTimeFormatter.ofPattern ("yyyy-MM-dd HH: mm"); javni CustomLocalDateTimeSerializer () {this (null); } javni CustomLocalDateTimeSerializer (klasa t) {super (t); } @Override public void serialize (vrijednost LocalDateTime, JsonGenerator gen, SerializerProvider arg2) baca IOException, JsonProcessingException {gen.writeString (formatter.format (value)); }}

Dalje - upotrijebimo serializator za naš “datum događajaPolje:

javni razred Događaj {naziv javnog niza; @JsonSerialize (koristeći = CustomLocalDateTimeSerializer.class) javni LocalDateTime eventDate; }

Sada - testirajmo:

@Test public void whenSerializingJava8DateWithCustomSerializer_thenCorrect () baca JsonProcessingException {LocalDateTime date = LocalDateTime.of (2014, 12, 20, 2, 30); Događaj događaja = novi Događaj ("zabava", datum); Mapa ObjectMapper = novi ObjectMapper (); Rezultat niza = mapper.writeValueAsString (događaj); assertThat (rezultat, sadržiString ("2014-12-20 02:30")); }

11. Deserijalizirajte Datum

Dalje - pogledajmo kako deserializirati a Datum s Jackson. U sljedećem primjeru - deserijaliziramo "Događaj”Instanca koja sadrži datum:

@Test public void whenDeserializingDateWithJackson_thenCorrect () baca JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 20-12-2014 02:30:00 "}"; SimpleDateFormat df = novi SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); Mapa ObjectMapper = novi ObjectMapper (); mapper.setDateFormat (df); Događaj događaja = mapper.readerFor (Event.class) .readValue (json); assertEquals ("20-12-2014 02:30:00", df.format (event.eventDate)); }

12. Deserijalizirajte Joda ZonedDateTime Sa očuvanom vremenskom zonom

U svojoj zadanoj konfiguraciji, Jackson podešava vremensku zonu Jode ZonedDateTime vremenskoj zoni lokalnog konteksta. Kako, prema zadanim postavkama, vremenska zona lokalnog konteksta nije postavljena i mora se ručno konfigurirati, Jackson prilagođava vremensku zonu GMT:

@Test public void whenDeserialisingZonedDateTimeWithDefaults_thenNotCorrect () baca IOException {ObjectMapper objectMapper = new ObjectMapper (); objectMapper.findAndRegisterModules (); objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); ZonedDateTime sada = ZonedDateTime.now (ZoneId.of ("Europa / Berlin")); Niz pretvoren = objectMapper.writeValueAsString (sada); ZonedDateTime obnovljeno = objectMapper.readValue (pretvoreno, ZonedDateTime.class); System.out.println ("serialized:" + now); System.out.println ("obnovljeno:" + vraćeno); assertThat (sada je (obnovljeno)); }

Ovaj test slučaj neće uspjeti s izlazom:

serializirano: 2017-08-14T13: 52: 22.071 + 02: 00 [Europa / Berlin] obnovljeno: 2017-08-14T11: 52: 22.071Z [UTC]

Srećom, postoji brzo i jednostavno rješenje za ovo neobično zadano ponašanje: samo moramo reći Jacksonu, da ne prilagođava vremensku zonu.

To se može učiniti dodavanjem donjeg retka koda u gornji testni slučaj:

objectMapper.disable (DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

Imajte na umu da, da bismo sačuvali vremensku zonu, također moramo onemogućiti zadano ponašanje serializiranja datuma na vremensku oznaku.

13. Prilagođeno Datum Deserijalizator

Pogledajmo i kako koristiti običaj Datum deserijalizator; napisat ćemo prilagođeni deserijalizator za svojstvo “datum događaja“:

javna klasa CustomDateDeserializer proširuje StdDeserializer {private SimpleDateFormat formatter = new SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); javni CustomDateDeserializer () {this (null); } javni CustomDateDeserializer (klasa vc) {super (vc); } @Override public Date deserialize (JsonParser jsonparser, DeserializationContext context) baca IOException, JsonProcessingException {Datum niza = jsonparser.getText (); pokušajte {return formatter.parse (datum); } catch (ParseException e) {throw new RuntimeException (e); }}}

Dalje - upotrijebimo ga kao „datum događaja”Deserijalizator:

javni razred Događaj {naziv javnog niza; @JsonDeserialize (koristeći = CustomDateDeserializer.class) javni datum eventDate; }

I na kraju - testirajmo:

@Test public void whenDeserializingDateUsingCustomDeserializer_thenCorrect () baca JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 20-12-2014 02:30:00 "}"; SimpleDateFormat df = novi SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); Mapa ObjectMapper = novi ObjectMapper (); Događaj događaja = mapper.readerFor (Event.class) .readValue (json); assertEquals ("20-12-2014 02:30:00", df.format (event.eventDate)); }

14. Učvršćivanje InvalidDefinitionIznimka

Prilikom stvaranja a LocalDate na primjer, možemo naići na iznimku:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Nije moguće konstruirati instancu `java.time.LocalDate` (ne postoje tvorci, poput zadane konstrukcije): nema konstruktorskog argumenta niza / tvorničke metode za deserializaciju iz vrijednosti niza ('2014 -12-20 ') na [Izvor: (Niz) "2014-12-20"; redak: 1, stupac: 1]

Do ovog problema dolazi jer JSON izvorno nema format datuma, pa predstavlja datume kao Niz.

The Niz predstavljanje datuma nije isto što i objekt tipa LocalDate u memoriji, pa nam je potreban vanjski deserijalizator za čitanje tog polja iz a Nizi serializator za prikaz datuma Niz format.

Ove se metode također primjenjuju na LocalDateTime - jedina promjena je upotreba ekvivalentne klase za LocalDateTime.

14.1. Jacksonova ovisnost

Jackson nam omogućuje da to popravimo na nekoliko načina. Prvo, moramo se pobrinuti za jsr310 ovisnost je u našem pom.xml:

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

14.2. Serijalizacija na objekt s jednim datumom

Da bi se mogao nositi LocalDate, moramo registrirati JavaTimeModule s našim ObjectMapper.

Također onemogućujemo značajku WRITE_DATES_AS_TIMESTAMPS u ObjectMapper da spriječite Jacksona da doda vremenske znamenke na JSON izlaz:

@Test public void whenSerializingJava8DateAndReadingValue_thenCorrect () baca IOException {String stringDate = "\" 2014-12-20 \ ""; Mapa ObjectMapper = novi ObjectMapper (); mapper.registerModule (novi JavaTimeModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Rezultat LocalDate = mapper.readValue (stringDate, LocalDate.class); assertThat (result.toString (), containsString ("20. 12. 2014.")); }

Ovdje smo koristili Jacksonovu izvornu podršku za serializiranje i deserializiranje datuma.

14.3. Bilješka u POJO

Drugi način rješavanja tog problema je korištenje LocalDateDeserializer i JsonFormat bilješke na razini entiteta:

javna klasa EventWithLocalDate {@JsonDeserialize (pomoću = LocalDateDeserializer.class) @JsonSerialize (pomoću = LocalDateSerializer.class) @JsonFormat (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") javni LocalDate eventDate; }

The @JsonDeserialize napomena se koristi za specificiranje prilagođenog deserijalizatora za uklanjanje JSON objekta. Slično tome, @JsonSerialize označava prilagođeni serializator koji se koristi prilikom marširanja entiteta.

Uz to i bilješka @JsonFormat omogućuje nam da odredimo format u koji ćemo serializirati vrijednosti datuma. Stoga se ovaj POJO može koristiti za čitanje i pisanje JSON-a:

@Test public void whenSerializingJava8DateAndReadingFromEntity_thenCorrect () baca IOException {String json = "{\" name \ ": \" party \ ", \" eventDate \ ": \" 20-12-2014 \ "}"; Mapa ObjectMapper = novi ObjectMapper (); EventWithLocalDate rezultat = mapper.readValue (json, EventWithLocalDate.class); assertThat (result.getEventDate (). toString (), containsString ("20.12.2014.")); }

Iako ovaj pristup zahtijeva više posla nego korištenje JavaTimeModule zadane postavke, puno je prilagodljiviji.

15. Zaključak

U ovom opsežnom Datum članak, pogledali smo nekoliko načina Jackson može pomoći maršalu i odmarširati sastanak s JSON-om koristeći razuman format nad kojim imamo kontrolu.

Kao i uvijek, primjeri koda mogu se naći na GitHubu.


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