ג'קסון דייט

1. סקירה כללית

במדריך זה נסדר תאריכים עם ג'קסון. נתחיל בסידור Java.util פשוט.תַאֲרִיךואז ג'ודה-טיים כמו גם ג'אווה 8 תאריך שעה.

2. סדר סדר תַאֲרִיך לחותמת זמן

ראשית - בואו נראה איך לסדר סדר פשוט java.util.Date עם ג'קסון.

בדוגמה הבאה - נבצע סדרת מופע של "מִקרֶה"שיש בו תַאֲרִיך שדה "תאריך אירוע“:

@Test ציבורי בטל כאשרSerializingDateWithJackson_thenSerializedToTimestamp () זורק JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); תאריך תאריך = df.parse ("01-01-1970 01:00"); אירוע אירוע = אירוע חדש ("מסיבה", תאריך); ממפה ObjectMapper = ObjectMapper חדש (); mapper.writeValueAsString (אירוע); }

מה שחשוב כאן הוא שג'קסון יסדר את התאריך בפורמט חותמת זמן כברירת מחדל (מספר אלפיות השנייה מאז 1 בינואר 1970, UTC).

התפוקה בפועל של "מִקרֶהסידור הוא:

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

3. סדר סדר תַאֲרִיך ל- ISO-8601

ההתייחסות לפורמט חותמת הזמן הקצרה הזו אינה אופטימלית. בואו בואו עכשיו לסדר את ה- תַאֲרִיך אל ה ISO-8601 פוּרמָט:

@Test הציבור בטל כאשרSerializingDateToISO8601_thenSerializedToText () זורק JsonProcessingException, ParseException {SimpleDateFormat df = חדש SimpleDateFormat ("dd-MM-yyyy hh: mm"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); מחרוזת toParse = "01-01-1970 02:30"; תאריך תאריך = df.parse (toParse); אירוע אירוע = אירוע חדש ("מסיבה", תאריך); ממפה ObjectMapper = ObjectMapper חדש (); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // StdDateFormat הוא ISO8601 מאז jackson 2.9 mapper.setDateFormat (חדש StdDateFormat (). WithColonInTimeZone (נכון)); תוצאת מחרוזת = mapper.writeValueAsString (אירוע); assertThat (תוצאה, containString ("1970-01-01T02: 30: 00.000 + 00: 00")); }

שים לב עד כמה ייצוג התאריך הרבה יותר קריא.

4. הגדר ObjectMapperפורמט תאריך

הפתרונות הקודמים עדיין חסרים את הגמישות המלאה בבחירת הפורמט המדויק לייצוג ה- java.util.Date מקרים.

בואו נסתכל כעת על תצורה שתאפשר לנו הגדר את הפורמטים שלנו לייצוג תאריכים:

@Test ציבורי בטל כאשרSettingObjectMapperDateFormat_thenCorrect () זורק JsonProcessingException, ParseException {SimpleDateFormat df = חדש SimpleDateFormat ("dd-MM-yyyy hh: mm"); מחרוזת toParse = "20-12-2014 02:30"; תאריך תאריך = df.parse (toParse); אירוע אירוע = אירוע חדש ("מסיבה", תאריך); ממפה ObjectMapper = ObjectMapper חדש (); mapper.setDateFormat (df); תוצאת מחרוזת = mapper.writeValueAsString (אירוע); assertThat (תוצאה, containString (toParse)); }

שים לב שלמרות שאנחנו עכשיו גמישים יותר בנוגע לפורמט התאריך - אנחנו עדיין משתמשים בתצורה גלובלית ברמה כולה ObjectMapper.

5. השתמש @JsonFormat לפרמט תַאֲרִיך

לאחר מכן, בואו נסתכל על ה- @JsonFormat ביאור ל לשלוט בפורמט התאריכים בשיעורים בודדים במקום גלובלי, לכל היישום:

אירוע בכיתה ציבורית {ציבורי שם מחרוזת; @JsonFormat (צורה = JsonFormat.Shape.STRING, תבנית = "dd-MM-yyyy hh: mm: ss") ציבורי תאריך eventDate; }

עכשיו - בואו נבדוק את זה:

@Test ציבורי בטל כאשרUsingJsonFormatAnnotationToFormatDate_thenCorrect () זורק JsonProcessingException, ParseException {SimpleDateFormat df = new SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); df.setTimeZone (TimeZone.getTimeZone ("UTC")); מחרוזת toParse = "20-12-2014 02:30:00"; תאריך תאריך = df.parse (toParse); אירוע אירוע = אירוע חדש ("מסיבה", תאריך); ממפה ObjectMapper = ObjectMapper חדש (); תוצאת מחרוזת = mapper.writeValueAsString (אירוע); assertThat (תוצאה, containString (toParse)); }

6. מותאם אישית תַאֲרִיך Serializer

לאחר מכן - כדי לקבל שליטה מלאה על הפלט, ננצל סדרת סדר מותאמת אישית לתאריכים:

מחלקה ציבורית CustomDateSerializer מרחיב StdSerializer {פרטי SimpleDateFormat מעצב = חדש SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); Public CustomDateSerializer () {this (null); } CustomDateSerializer ציבורי (Class t) {super (t); } @Override בטל ציבורי בטל סידור (ערך תאריך, JsonGenerator gen, SerializerProvider arg2) זורק IOException, JsonProcessingException {gen.writeString (formatter.format (value)); }}

הבא - בואו נשתמש בזה בתור הסידור של "תאריך אירוע" שדה:

אירוע בכיתה ציבורית {ציבורי שם מחרוזת; @JsonSerialize (באמצעות = CustomDateSerializer.class) תאריך ציבורי eventDate; }

לסיום - בואו לבדוק את זה:

@Test ציבורי בטל כאשרUsingCustomDateSerializer_thenCorrect () זורק JsonProcessingException, ParseException {SimpleDateFormat df = חדש SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); מחרוזת toParse = "20-12-2014 02:30:00"; תאריך תאריך = df.parse (toParse); אירוע אירוע = אירוע חדש ("מסיבה", תאריך); ממפה ObjectMapper = ObjectMapper חדש (); תוצאת מחרוזת = mapper.writeValueAsString (אירוע); assertThat (תוצאה, containString (toParse)); }

7. סדר סדר ג'ודה-טיים עם ג'קסון

תאריכים הם לא תמיד מופע של java.util.Date; למעשה - הם מיוצגים יותר ויותר על ידי מעמד אחר - ונפוץ הוא, כמובן, ה תאריך שעה יישום מספריית Joda-Time.

בואו נראה איך אנחנו יכולים לסדר תאריך שעה עם ג'קסון.

נשתמש ב- jackson-datatype-joda מודול לתמיכה ב- Joda-Time מחוץ לקופסה:

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

ועכשיו אנחנו יכולים פשוט לרשום את JodaModule ותעשה:

@ מבחן ציבורי בטל כאשר SerializingJodaTime_thenCorrect () זורק את JsonProcessingException {DateTime date = DateTime new (2014, 12, 20, 2, 30, DateTimeZone.forID ("Europe / London")); ממפה ObjectMapper = ObjectMapper חדש (); mapper.registerModule (JodaModule חדש ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); תוצאת מחרוזת = mapper.writeValueAsString (תאריך); assertThat (תוצאה, containString ("2014-12-20T02: 30: 00.000Z")); }

8. סדר את ג'ודה תאריך שעה עם Serializer מותאם אישית

אם איננו רוצים את התלות הנוספת בג'ודה-טיים ג'קסון - נוכל גם להשתמש בה סידור מותאם אישית (בדומה לדוגמאות הקודמות) להשיג תאריך שעה מקרים מסודרים בצורה נקייה:

מחלקה ציבורית CustomDateTimeSerializer מרחיב StdSerializer {פרטי סטטי DateTimeFormatter מעצב = DateTimeFormat.forPattern ("yyyy-MM-dd HH: mm"); ציבורי CustomDateTimeSerializer () {this (null); } פומבי CustomDateTimeSerializer (Class t) {super (t); } @Override חלל ציבורי בסידור (ערך DateTime, JsonGenerator gen, SerializerProvider arg2) זורק IOException, JsonProcessingException {gen.writeString (formatter.print (value)); }}

הבא - בואו נשתמש בו כרכוש שלנו “תאריך אירוע”סידור:

אירוע בכיתה ציבורית {ציבורי שם מחרוזת; @JsonSerialize (באמצעות = CustomDateTimeSerializer.class) DateTime eventDate פומבי; }

לסיום - בואו נרכיב הכל ונבדוק את זה:

@Test הציבור בטל כאשרSerializingJodaTimeWithJackson_thenCorrect () זורק JsonProcessingException {DateTime תאריך = DateTime חדש (2014, 12, 20, 2, 30); אירוע אירוע = אירוע חדש ("מסיבה", תאריך); ממפה ObjectMapper = ObjectMapper חדש (); תוצאת מחרוזת = mapper.writeValueAsString (אירוע); assertThat (תוצאה, containString ("2014-12-20 02:30")); }

9. סידור Java 8 תַאֲרִיך עם ג'קסון

לאחר מכן - בואו נראה כיצד לסדר את Java 8 תאריך שעה - בדוגמה זו, LocalDateTime - באמצעות ג'קסון. אנחנו יכולים לעשות שימוש ב- jackson-datatype-jsr310 מודול:

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

כעת, כל שעלינו לעשות הוא לרשום את ה- JavaTimeModule (JSR310 מודול הוצא משימוש) וג'קסון ידאג לשאר:

@ מבחן ציבורי בטל כאשר SerializingJava8Date_thenCorrect () זורק JsonProcessingException {LocalDateTime תאריך = LocalDateTime.of (2014, 12, 20, 2, 30); ממפה ObjectMapper = ObjectMapper חדש (); mapper.registerModule (JavaTimeModule חדש ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); תוצאת מחרוזת = mapper.writeValueAsString (תאריך); assertThat (תוצאה, containString ("2014-12-20T02: 30")); }

10. סידרו את Java 8 תַאֲרִיך ללא כל תלות נוספת

אם אתה לא רוצה את התלות הנוספת, אתה תמיד יכול להשתמש סידור מותאם אישית כדי לכתוב את Java 8 תאריך שעה ל- JSON:

מחלקה ציבורית CustomLocalDateTimeSerializer מרחיב את StdSerializer {private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern ("yyyy-MM-dd HH: mm"); ציבורי CustomLocalDateTimeSerializer () {this (null); } CustomLocalDateTimeSerializer ציבורי (Class t) {super (t); } @Override חלל ציבורי בסידור (ערך LocalDateTime, JsonGenerator gen, SerializerProvider arg2) זורק IOException, JsonProcessingException {gen.writeString (formatter.format (value)); }}

הבא - בואו נשתמש בסדרת הסידור עבור "תאריך אירוע" שדה:

אירוע בכיתה ציבורית {ציבורי שם מחרוזת; @JsonSerialize (באמצעות = CustomLocalDateTimeSerializer.class) LocalDateTime eventDate ציבורי; }

עכשיו - בואו נבדוק את זה:

@Test ציבורי בטל כאשרSerializingJava8DateWithCustomSerializer_thenCorrect () זורק JsonProcessingException {LocalDateTime תאריך = LocalDateTime.of (2014, 12, 20, 2, 30); אירוע אירוע = אירוע חדש ("מסיבה", תאריך); ממפה ObjectMapper = ObjectMapper חדש (); תוצאת מחרוזת = mapper.writeValueAsString (אירוע); assertThat (תוצאה, containString ("2014-12-20 02:30")); }

11. התפטר תַאֲרִיך

לאחר מכן - בואו נראה איך לבטל את עריכת ה- a תַאֲרִיך עם ג'קסון. בדוגמה הבאה - אנו מבטלים עריכה של "מִקרֶהמופע המכיל תאריך:

@Test הציבור בטל כאשרDeserializingDateWithJackson_thenCorrect () זורק את JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 20-12-2014 02:30:00 "}"; SimpleDateFormat df = SimpleDateFormat חדש ("dd-MM-yyyy hh: mm: ss"); ממפה ObjectMapper = ObjectMapper חדש (); mapper.setDateFormat (df); אירוע אירוע = mapper.readerFor (Event.class) .readValue (json); assertEquals ("20-12-2014 02:30:00", df.format (event.eventDate)); }

12. התפטלו מהג'ודה ZonedDateTime עם שמירת אזור הזמן

בתצורת ברירת המחדל שלה, ג'קסון מתאים את אזור הזמן של ג'ודה ZonedDateTime לאזור הזמן של ההקשר המקומי. מכיוון, כברירת מחדל, אזור הזמן של ההקשר המקומי אינו מוגדר ויש להגדירו באופן ידני, ג'קסון מתאים את אזור הזמן ל- GMT:

@Test ציבורי בטל כאשרDeserialisingZonedDateTimeWithDefaults_thenNotCorrect () זורק IOException {ObjectMapper objectMapper = ObjectMapper חדש (); objectMapper.findAndRegisterModules (); objectMapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); ZonedDateTime now = ZonedDateTime.now (ZoneId.of ("אירופה / ברלין")); מחרוזת המרה = objectMapper.writeValueAsString (עכשיו); ZonedDateTime שוחזר = objectMapper.readValue (הומר, ZonedDateTime.class); System.out.println ("בסידרה:" + עכשיו); System.out.println ("משוחזר:" + משוחזר); assertThat (עכשיו, הוא (משוחזר)); }

מקרה הבדיקה הזה ייכשל עם הפלט:

סדרתי: 2017-08-14T13: 52: 22.071 + 02: 00 [אירופה / ברלין] משוחזר: 2017-08-14T11: 52: 22.071Z [UTC]

למרבה המזל, יש פתרון מהיר ופשוט להתנהגות ברירת המחדל המוזרה הזו: אנחנו רק צריכים לומר לג'קסון, לא להתאים את אזור הזמן.

ניתן לעשות זאת על ידי הוספת שורת הקוד למטה למקרה הבדיקה שלעיל:

objectMapper.disable (DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

שים לב שכדי לשמור על אזור הזמן עלינו להשבית את התנהגות ברירת המחדל של סדרת התאריך לחותמת הזמן.

13. מותאם אישית תַאֲרִיך מוריד עריקה

בואו נראה גם כיצד להשתמש תחפושת תַאֲרִיך deserializer; נכתוב מכשיר deserializer מותאם אישית עבור הנכס "תאריך אירוע“:

מחלקה ציבורית CustomDateDeserializer מרחיב StdDeserializer {פרטי SimpleDateFormat מעצב = חדש SimpleDateFormat ("dd-MM-yyyy hh: mm: ss"); ציבורי CustomDateDeserializer () {this (null); } CustomDateDeserializer ציבורי (Class vc) {super (vc); } @Override ציבורי deserialize תאריך (JsonParser jsonparser, DeserializationContext הקשר) זורק IOException, JsonProcessingException {תאריך מחרוזת = jsonparser.getText (); נסה {return formatter.parse (תאריך); } לתפוס (ParseException e) {לזרוק RuntimeException (e) חדש; }}}

הבא - בואו נשתמש בזה כ- “תאריך אירועDeserializer:

אירוע בכיתה ציבורית {ציבורי שם מחרוזת; @JsonDeserialize (באמצעות = CustomDateDeserializer.class) תאריך ציבורי eventDate; }

ולסיום - בואו נבדוק את זה:

@ מבחן פומבי בטל כאשרDeserializingDateUsingCustomDeserializer_thenCorrect () זורק את JsonProcessingException, IOException {String json = "{" name ":" party "," eventDate ":" 20-12-2014 02:30:00 "}"; SimpleDateFormat df = SimpleDateFormat חדש ("dd-MM-yyyy hh: mm: ss"); ממפה ObjectMapper = ObjectMapper חדש (); אירוע אירוע = mapper.readerFor (Event.class) .readValue (json); assertEquals ("20-12-2014 02:30:00", df.format (event.eventDate)); }

14. תיקון הגדרה לא חוקיתיוצא מן הכלל

בעת יצירת a LocalDate למשל, אנו עשויים להיתקל בחריג:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: לא ניתן לבנות מופע של 'java.time.LocalDate' (אין יוצרים, כמו מבנה ברירת מחדל, קיימים): אין קונסטרוקטור / שיטת מפעל מחרוזת לביטול הערך ממחרוזת ('2014 -12-20 ') ב- [מקור: (String) "2014-12-20"; שורה: 1, עמודה: 1]

בעיה זו מתרחשת מכיוון של- JSON אין פורמט תאריך, ולכן הוא מייצג תאריכים כמו חוּט.

ה חוּט ייצוג של תאריך אינו זהה לאובייקט מסוג LocalDate בזיכרון, לכן אנו זקוקים למפציץ deserialial חיצוני כדי לקרוא שדה זה מתוך חוּט, ו- Serializer למסור את התאריך חוּט פוּרמָט.

שיטות אלה חלות גם על LocalDateTime - השינוי היחיד הוא להשתמש בכיתה שווה ערך עבור LocalDateTime.

14.1. תלות ג'קסון

ג'קסון מאפשר לנו לתקן זאת בכמה דרכים. ראשית, עלינו לוודא שה- jsr310 התלות היא שלנו pom.xml:

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

14.2. סידור לאובייקט תאריך יחיד

על מנת להיות מסוגלים להתמודד LocalDate, אנחנו צריכים לרשום את JavaTimeModule עם שלנו ObjectMapper.

אנו גם משביתים את התכונה WRITE_DATES_AS_TIMESTAMPS ב ObjectMapper כדי למנוע מג'קסון להוסיף ספרות זמן לפלט JSON:

@ מבחן ציבורי בטל כאשר SerializingJava8DateAndReadingValue_thenCorrect () זורק IOException {String stringDate = "\" 2014-12-20 \ ""; ממפה ObjectMapper = ObjectMapper חדש (); mapper.registerModule (JavaTimeModule חדש ()); mapper.disable (SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); תוצאת LocalDate = mapper.readValue (stringDate, LocalDate.class); assertThat (result.toString (), containsString ("2014-12-20")); }

הנה, השתמשנו בתמיכה המקומית של ג'קסון לסידור תאריכים ולערוך את עריכתם.

14.3. ביאור ב- POJO

דרך נוספת להתמודד עם בעיה זו היא להשתמש ב- LocalDateDeserializer ו JsonFormat הערות ברמת הישות:

מחלקה ציבורית EventWithLocalDate {@JsonDeserialize (using = LocalDateDeserializer.class) @JsonSerialize (using = LocalDateSerializer.class) @JsonFormat (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyyy") LocalDate eventDate; }

ה @JsonDeserialize ביאור משמש כדי לציין deserializer מותאם אישית כדי לבטל את הרישום של אובייקט JSON. בדומה לכך, @JsonSerialize מציין מכשיר סדרתי מותאם אישית לשימוש בעת ביצוע יישום.

בנוסף, ההערה @JsonFormat מאפשר לנו לציין את הפורמט אליו נקבל סדרת ערכי תאריך. לכן, בעזרת POJO זה ניתן לקרוא ולכתוב את ה- JSON:

@ מבחן פומבי בטל כאשר SerializingJava8DateAndReadingFromEntity_thenCorrect () זורק IOException {String json = "{\" name \ ": \" party \ ", \" eventDate \ ": \" 20-12-2014 \ "}"; ממפה ObjectMapper = ObjectMapper חדש (); EventWithLocalDate result = mapper.readValue (json, EventWithLocalDate.class); assertThat (result.getEventDate (). toString (), containString ("2014-12-20")); }

גישה זו אמנם דורשת יותר עבודה מאשר השימוש ב- JavaTimeModule ברירות מחדל, זה הרבה יותר להתאמה אישית.

15. מסקנה

בהרחבה זו תַאֲרִיך מאמר, בדקנו כמה דרכים ג'קסון יכול לעזור למרשל ולבטל דייט ב- JSON באמצעות פורמט הגיוני שיש לנו שליטה עליו.

כמו תמיד, את קוד הדוגמאות ניתן למצוא ב- GitHub.


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