Jackson randevú
1. Áttekintés
Ebben az oktatóanyagban sorosítjuk a dátumokat Jackson-szal. Kezdjük egy egyszerű java.util sorosításával.Dátum, majd a Joda-Time, valamint a Java 8 Dátum idő.
2. Sorosítson Dátum az Időbélyegzőhöz
Először - nézzük meg, hogyan sorosíthatunk egy egyszerűt java.util.Dátum Jacksonnal.
A következő példában - sorosítjuk a „Esemény”Amelynek van Dátum terület "esemény dátum“:
@Test public void whenSerializingDateWithJackson_thenSerializedToTimestamp () dobja a JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-éééé hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); Dátum dátum = df.parse ("1970-01-01 01:00"); Esemény esemény = új esemény ("party", dátum); ObjectMapper mapper = új ObjectMapper (); mapper.writeValueAsString (esemény); }
Ami itt fontos, hogy Jackson a Dátumot alapértelmezés szerint időbélyeg formátumba sorolja (ezredmásodpercek száma 1970. január 1. óta, UTC).
A „esemény”A sorosítás:
{"name": "party", "eventDate": 3600000}
3. Sorosítson Dátum az ISO-8601 szerint
Erre a rövid időbélyeg-formátumra való sorosítás nem optimális. Sorosítsuk most a Dátum hoz ISO-8601 formátum:
@Test public void whenSerializingDateToISO8601_thenSerializedToText () dobja a JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-éééé hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); String toParse = "1970-01-01 02:30"; Dátum dátum = df.parse (toParse); Esemény esemény = új esemény ("party", dátum); ObjectMapper mapper = új ObjectMapper (); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // Az StdDateFormat ISO8601, mivel a jackson 2.9 mapper.setDateFormat (új StdDateFormat (). WithColonInTimeZone (true)); Karakterlánc eredménye = mapper.writeValueAsString (esemény); assertThat (eredmény, tartalmazzaString ("1970-01-01T02: 30: 00.000 + 00: 00")); }
Vegye figyelembe, hogy a dátum ábrázolása hogyan olvashatóbb.
4. Konfigurálja ObjectMapperDátum formátum
A korábbi megoldások még mindig nem rendelkeznek teljes rugalmassággal a pontos formátum kiválasztására az ábrázoláshoz java.util.Dátum példányok.
Most nézzünk meg egy konfigurációt, amely lehetővé teszi számunkra állítsa be a dátumokat ábrázoló formátumunkat:
@Test public void whenSettingObjectMapperDateFormat_thenCorrect () dobja a JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm"); String toParse = "2014.12.20. 02:30"; Dátum dátum = df.parse (toParse); Esemény esemény = új esemény ("party", dátum); ObjectMapper mapper = új ObjectMapper (); mapper.setDateFormat (df); Karakterlánc eredménye = mapper.writeValueAsString (esemény); assertThat (eredmény, tartalmazzaString (toParse)); }
Vegye figyelembe, hogy annak ellenére, hogy mostanra rugalmasabbak vagyunk a dátumformátum tekintetében - továbbra is globális konfigurációt használunk az egész szintjén ObjectMapper.
5. Használja @JsonFormat a Formázáshoz Dátum
Ezután vessünk egy pillantást a @JsonFormat annotáció a ellenőrizheti a dátum formátumát az egyes osztályokban globálisan, a teljes alkalmazás esetében:
public class Esemény {public String name; @JsonFormat (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh: mm: ss") public Date eventDate; }
Most - teszteljük:
@Test public void whenUsingJsonFormatAnnotationToFormatDate_thenCorrect () dobja a JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-éééé hh: mm: ss"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); String toParse = "2014.12.20. 02:30:00"; Dátum dátum = df.parse (toParse); Esemény esemény = új esemény ("party", dátum); ObjectMapper mapper = új ObjectMapper (); Karakterlánc eredménye = mapper.writeValueAsString (esemény); assertThat (eredmény, tartalmazzaString (toParse)); }
6. Egyedi Dátum Serializer
Következő - a kimenet teljes ellenőrzése érdekében a Dates egyedi sorosítóját használjuk:
public class A CustomDateSerializer kiterjeszti a StdSerializer {privát SimpleDateFormat formázó = új SimpleDateFormat ("dd-MM-éééé óó: pp: ss"); public CustomDateSerializer () {this (null); } public CustomDateSerializer (t osztály) {super (t); } @Orride public void serialize (Dátumérték, JsonGenerator gen, SerializerProvider arg2) dobja az IOException, JsonProcessingException {gen.writeString (formatter.format (érték)); }}
Következő - használjuk „esemény dátum" terület:
public class Esemény {public String name; @JsonSerialize (using = CustomDateSerializer.class) public Date eventDate; }
Végül - teszteljük:
@Test public void whenUsingCustomDateSerializer_thenCorrect () dobja a JsonProcessingException, ParseException {SimpleDateFormat df = új SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); String toParse = "2014.12.20. 02:30:00"; Dátum dátum = df.parse (toParse); Esemény esemény = új esemény ("party", dátum); ObjectMapper mapper = új ObjectMapper (); Karakterlánc eredménye = mapper.writeValueAsString (esemény); assertThat (eredmény, tartalmazzaString (toParse)); }
7. Serializálja a Joda-Time-ot Jacksonnal
A dátumok nem mindig jelennek meg java.util.Dátum; valójában - egyre inkább képviseli őket valamilyen más osztály - és egy közös természetesen az Dátum idő megvalósítás a Joda-Time könyvtárból.
Lássuk, hogyan tudunk sorosítani Dátum idő Jacksonnal.
Ki fogjuk használni a jackson-adattípus-joda modul a dobozon kívüli Joda-Time támogatáshoz:
com.fasterxml.jackson.datatype jackson-datatype-joda 2.9.7
És most egyszerűen regisztrálhatjuk a JodaModule és kész:
@Test public void whenSerializingJodaTime_thenCorrect () dobja a JsonProcessingException {DateTime date = new DateTime (2014, 12, 20, 2, 30, DateTimeZone.forID ("Európa / London")); ObjectMapper mapper = új ObjectMapper (); mapper.registerModule (új JodaModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Karakterlánc eredménye = mapper.writeValueAsString (dátum); assertThat (eredmény, tartalmazzaString ("2014-12-20T02: 30: 00.000Z")); }
8. Serializálja a Jodát Dátum idő Egyéni Serializerrel
Ha nem akarunk extra Joda-Time Jackson-függőséget - használhatjuk is egyedi sorosító (hasonlóan a korábbi példákhoz) Dátum idő tisztán sorosított példányok:
public class A CustomDateTimeSerializer kiterjeszti az StdSerializer {private static DateTimeFormatter formatter = DateTimeFormat.forPattern ("éééé-hh-nn ÓÓ: mm"); public CustomDateTimeSerializer () {this (null); } public CustomDateTimeSerializer (t osztály) {super (t); } @Orride public void serialize (DateTime value, JsonGenerator gen, SerializerProvider arg2) dobja az IOException, JsonProcessingException {gen.writeString (formatter.print (érték)); }}
Következő - használjuk tulajdonként ”esemény dátum”Sorosító:
public class Esemény {public String name; @JsonSerialize (using = CustomDateTimeSerializer.class) public DateTime eventDate; }
Végül - rakjunk össze mindent és teszteljük:
@Test public void whenSerializingJodaTimeWithJackson_thenCorrect () dobja a JsonProcessingException {DateTime date = new DateTime (2014, 12, 20, 2, 30); Esemény esemény = új esemény ("party", dátum); ObjectMapper mapper = új ObjectMapper (); Karakterlánc eredménye = mapper.writeValueAsString (esemény); assertThat (eredmény, tartalmazzaString ("2014-12-20 02:30")); }
9. Serialize Java 8 Dátum Jacksonnal
Következő - lássuk, hogyan kell sorosítani a Java 8-at Dátum idő - ebben a példában LocalDateTime - Jackson segítségével. Használhatjuk a jackson-datatípus-jsr310 modul:
com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.9.7
Most csak annyit kell tennünk, hogy regisztráljuk a JavaTimeModule (JSR310Modul elavult) és Jackson gondoskodik a többiről:
@Test public void whenSerializingJava8Date_thenCorrect () dobja a JsonProcessingException {LocalDateTime date = LocalDateTime.of (2014, 12, 20, 2, 30); ObjectMapper mapper = új ObjectMapper (); mapper.registerModule (új JavaTimeModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); Karakterlánc eredménye = mapper.writeValueAsString (dátum); assertThat (eredmény, tartalmazzaString ("2014-12-20T02: 30")); }
10. Serializálja a Java 8-at Dátum Minden különös függőség nélkül
Ha nem akarja az extra függőséget, mindig használhatja egyedi sorosító a Java 8 kiírására Dátum idő JSON-nak:
public class CustomLocalDateTimeSerializer kiterjeszti a StdSerializer {private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern ("éééé-hh-nn ÓÓ: mm"); public CustomLocalDateTimeSerializer () {this (null); } public CustomLocalDateTimeSerializer (t osztály) {super (t); } @Orride public void serialize (LocalDateTime érték, JsonGenerator gen, SerializerProvider arg2) dobja az IOException, JsonProcessingException {gen.writeString (formatter.format (érték)); }}
Következő - használjuk a sorosítót aesemény dátum" terület:
public class Esemény {public String name; @JsonSerialize (using = CustomLocalDateTimeSerializer.class) public LocalDateTime eventDate; }
Most - teszteljük:
@Test public void whenSerializingJava8DateWithCustomSerializer_thenCorrect () dobja a JsonProcessingException {LocalDateTime date = LocalDateTime.of (2014, 12, 20, 2, 30); Esemény esemény = új esemény ("party", dátum); ObjectMapper mapper = új ObjectMapper (); Karakterlánc eredménye = mapper.writeValueAsString (esemény); assertThat (eredmény, tartalmazzaString ("2014-12-20 02:30")); }
11. Deserializáljon Dátum
Következő - nézzük meg, hogyan lehet deserializálni a Dátum val vel Jackson. A következő példában - deserializálunk egy „Esemény”Dátumot tartalmazó példány:
@Test public void whenDeserializingDateWithJackson_thenCorrect () dobja a JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 2014-12-20 02:30:00 "}"; SimpleDateFormat df = új SimpleDateFormat ("éééé-hh-éééé óó: pp: ss"); ObjectMapper mapper = új ObjectMapper (); mapper.setDateFormat (df); Esemény esemény = mapper.readerFor (Event.class) .readValue (json); assertEquals ("2014.12.20. 02:30:00", df.format (event.eventDate)); }
12. Dezerializálja Jodát ZonedDateTime Megőrzött időzónával
Alapértelmezés szerint Jackson beállítja a Joda időzónáját ZonedDateTime a helyi kontextus időzónájához. Mivel alapértelmezés szerint a helyi kontextus időzónája nincs beállítva, és manuálisan kell konfigurálni, Jackson az időzónát GMT-re állítja:
@Test public void whenDeserialisingZonedDateTimeWithDefaults_thenNotCorrect () dobja az IOException {ObjectMapper objectMapper = new ObjectMapper (); objectMapper.findAndRegisterModules (); objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); ZonedDateTime now = ZonedDateTime.now (ZoneId.of ("Európa / Berlin")); Karakterlánc átalakítva = objectMapper.writeValueAsString (most); ZonedDateTime helyreállítva = objectMapper.readValue (konvertálva, ZonedDateTime.class); System.out.println ("sorosítva:" + most); System.out.println ("helyreállítva:" + visszaállítva); assertThat (most van (visszaállítva)); }
Ez a tesztesemény meghiúsul a kimenettel:
sorosítva: 2017-08-14T13: 52: 22.071 + 02: 00 [Európa / Berlin] visszaállítva: 2017-08-14T11: 52: 22.071Z [UTC]
Szerencsére van egy gyors és egyszerű javítás erre a furcsa alapértelmezett viselkedésre: csak el kell mondanunk Jacksonnak, hogy ne állítsuk be az időzónát.
Ez úgy tehető meg, hogy az alábbi kódsort hozzáadja a fenti tesztesethez:
objectMapper.disable (DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
Ne feledje, hogy az időzóna megőrzése érdekében le kell tiltanunk a dátum időbélyegzővé történő sorosításának alapértelmezett viselkedését is.
13. Egyedi Dátum Deserializer
Lássuk azt is, hogyan kell használni szokás Dátum deserializer; írunk egy egyedi deserializer-t az ingatlanhoz “esemény dátum“:
public class CustomDateDeserializer kiterjeszti a StdDeserializer {privát SimpleDateFormat formázó = új SimpleDateFormat ("dd-MM-éééé óó: pp: ss"); public CustomDateDeserializer () {this (null); } public CustomDateDeserializer (vc osztály) {super (vc); } @Orride public Date deserialize (JsonParser jsonparser, DeserializationContext context) dobja az IOException, JsonProcessingException {String date = jsonparser.getText (); próbáld ki a {return formatter.parse (dátum); } catch (ParseException e) {dobjon új RuntimeException (e); }}}
Következő - használjuk „esemény dátumDeserializer:
public class Esemény {public String name; @JsonDeserialize (using = CustomDateDeserializer.class) public Date eventDate; }
És végül - teszteljük:
@Test public void whenDeserializingDateUsingCustomDeserializer_thenCorrect () dobja a JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 2014-12-20 02:30:00 "}"; SimpleDateFormat df = új SimpleDateFormat ("éééé-hh-éééé óó: pp: ss"); ObjectMapper mapper = új ObjectMapper (); Esemény esemény = mapper.readerFor (Event.class) .readValue (json); assertEquals ("2014.12.20. 02:30:00", df.format (event.eventDate)); }
14. Rögzítés InvalidDefinitionKivétel
A LocalDate Például találkozhatunk egy kivétellel:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Nem lehet elkészíteni a `java.time.LocalDate` példányát (nincsenek alkotók, mint például az alapértelmezett konstrukció): nincs String-argumentum konstruktor / gyári módszer a String értékből deserializálni ('2014 -12-20 ') a [Forrás: (String) "2014-12-20"; sor: 1, oszlop: 1]
Ez a probléma azért merül fel, mert a JSON-nak nincs natív dátumformátuma, ezért a dátumokat mint Húr.
A Húr a dátum ábrázolása nem azonos típusú objektummal LocalDate a memóriában, ezért szükségünk van egy külső deserializátorra, hogy leolvassuk azt a mezőt a-ból Húr, és egy sorosítót a dátum visszaadásához Húr formátum.
Ezek a módszerek a következőkre is vonatkoznak LocalDateTime - az egyetlen változás az egyenértékű osztály használata a LocalDateTime.
14.1. Jackson-függőség
Jackson lehetővé teszi számunkra, hogy ezt néhány módon kijavítsuk. Először is meg kell győződnünk a jsr310 függőség a mi pom.xml:
com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.11.0
14.2. Sorosítás egyetlen dátumú objektumra
Annak érdekében, hogy kezelni tudja LocalDate, regisztrálnunk kell a JavaTimeModule velünk ObjectMapper.
A funkciót is letiltjuk WRITE_DATES_AS_TIMESTAMPS ban ben ObjectMapper megakadályozni, hogy Jackson időszámokat adjon a JSON kimenethez:
@Test public void whenSerializingJava8DateAndReadingValue_thenCorrect () dobja az IOException {String stringDate = "\" 2014-12-20 \ ""; ObjectMapper mapper = új ObjectMapper (); mapper.registerModule (új JavaTimeModule ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); LocalDate eredmény = mapper.readValue (stringDate, LocalDate.class); assertThat (eredmény.toString (), tartalmazzaString ("2014-12-20")); }
Itt felhasználtuk Jackson natív támogatását a dátumok sorosításához és dezerializálásához.
14.3. Megjegyzés a POJO-ban
A probléma kezelésének másik módja a LocalDateDeserializer és JsonFormat jelölések entitás szinten:
public class EventWithLocalDate {@JsonDeserialize (using = LocalDateDeserializer.class) @JsonSerialize (using = LocalDateSerializer.class) @JsonFormat (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy") public LocalDate eventDate; }
A @JsonDeserialize annotációval megadható egy egyedi deserializer a JSON objektum széthúzásához. Hasonlóképpen, @JsonSerialize az entitás marsallálásakor használandó egyedi sorosítót jelöl.
Ezen felül az annotáció @JsonFormat lehetővé teszi számunkra, hogy meghatározzuk a dátumértékek sorosítási formátumát. Ezért ez a POJO használható a JSON beolvasására és megírására:
@Test public void whenSerializingJava8DateAndReadingFromEntity_thenCorrect () IOException {String json = "{\" name \ ": \" party \ ", \" eventDate \ ": \" 20-12-2014 \ "}"; ObjectMapper mapper = új ObjectMapper (); EventWithLocalDate eredmény = mapper.readValue (json, EventWithLocalDate.class); assertThat (result.getEventDate (). toString (), tartalmazString ("2014-12-20")); }
Bár ez a megközelítés több munkát igényel, mint a JavaTimeModule alapértelmezés szerint sokkal testreszabhatóbb.
15. Következtetés
Ebben a kiterjedt Dátum cikket, többféleképpen is megvizsgáltuk Jackson segíthet a marsallnak és a marhának a JSON-ra való randevúban egy értelmes formátum használatával, amely felett rendelkezünk.
Mint mindig, a példakód megtalálható a GitHubon.