ירושה עם ג'קסון

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

במאמר זה נבחן עבודה עם היררכיות מעמדיות בג'קסון.

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

2. הכללת מידע על תת-סוג

ישנן שתי דרכים להוסיף מידע על סוג בעת סדרת וניתוק עריכת אובייקטים נתונים, כלומר הקלדת ברירת מחדל גלובלית והערות לפי מחלקה.

2.1. הקלדת ברירת מחדל גלובלית

שלושת שיעורי Java הבאים ישמשו להמחשת הכללה גלובלית של מטא נתונים מסוג.

רכב מעמד-על:

שיעור מופשט ציבורי רכב {פרטי מיתרים פרטיים; מודל מחרוזת פרטי; רכב מוגן (מחרוזת, מודל מחרוזת) {this.make = make; this.model = מודל; } // בונה ללא-ארג, גטרים וקובעים}

אוטו תת מחלקה:

רכב ציבורי ממשיך להרחיב את הרכב {ישיבה פרטית intCapacity; טופ זוגי פרטי מהיר; רכב ציבורי (תווית מחרוזת, דגם מחרוזת, int seatCapacity, כפול מהיר כפול) {super (make, model); this.seatingCapacity = sittingCapacity; this.topSpeed ​​= topSpeed; } // בונה ללא-ארג, גטרים וקובעים}

מַשָׂאִית תת מחלקה:

משאית ממעמד ציבורי מרחיבה את הרכב {מטען כפול פרטי כפול; משאית ציבורית (מחרוזת, מודל מחרוזת, מטען כפול קיבולת) {סופר (יצרן, מודל); this.payloadCapacity = payloadCapacity; } // בונה ללא-ארג, גטרים וקובעים}

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

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

צי ציבורי ברמה ציבורית {רכבי רשימה פרטית; // גטרים וקובעים}

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

ObjectMapper.enableDefaultTyping (ישימות ObjectMapper.DefaultTyping, JsonTypeInfo.As includeAs)

ה יָשִׂימוּת הפרמטר קובע את הסוגים הדורשים מידע על סוג, ואת includeAs הפרמטר הוא המנגנון להכללת מטא-נתונים מסוג. בנוסף, שתי גרסאות אחרות של enableDefaultTyping שיטה מסופקת:

  • ObjectMapper.enableDefaultTyping (יישום ObjectMapper.DefaultTyping): מאפשר למתקשר לציין את יָשִׂימוּת, תוך כדי שימוש WRAPPER_ARRAY כערך ברירת המחדל עבור includeAs
  • ObjectMapper.enableDefaultTyping (): שימושים OBJECT_AND_NON_CONCRETE כערך ברירת המחדל עבור יָשִׂימוּת ו WRAPPER_ARRAY כערך ברירת המחדל עבור includeAs

בואו נראה איך זה עובד. כדי להתחיל, עלינו ליצור ObjectMapper התנגד והפעל הקלדת ברירת מחדל בו:

ממפה ObjectMapper = ObjectMapper חדש (); mapper.enableDefaultTyping ();

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

מכונית רכב = מכונית חדשה ("מרצדס בנץ", "S500", 5, 250.0); משאית משאית = משאית חדשה ("איסוזו", "NQR", 7500.0); רשימת רכבים = ArrayList חדש (); כלי רכב. להוסיף (רכב); כלי רכב. להוסיף (משאית); צי סידור צי = צי חדש (); serializedFleet.setVehicles (כלי רכב);

אובייקטים מאוכלסים אלה יועברו לסידורי:

מחרוזת jsonDataString = mapper.writeValueAsString (serializedFleet);

מחרוזת JSON שהתקבלה:

{"כלי רכב": ["java.util.ArrayList", [["org.baeldung.jackson.inheritance.Car", {"make": "Mercedes-Benz", "model": "S500", "sittingCapacity" : 5, "topSpeed": 250.0}], ["org.baeldung.jackson.inheritance.Truck", {"make": "Isuzu", "model": "NQR", "payloadCapacity": 7500.0}]]] }

במהלך עריקת ייעול, אובייקטים משוחזרים ממחרוזת JSON עם נתוני סוג נשמרים:

צי deserializedFleet = mapper.readValue (jsonDataString, Fleet.class);

האובייקטים המשוחזרים יהיו מאותם תתי-סוגים קונקרטיים שהיו לפני סדרת הסידור:

assertThat (deserializedFleet.getVehicles (). get (0), instanceOf (Car.class)); assertThat (deserializedFleet.getVehicles (). get (1), instanceOf (Truck.class));

2.2. הערות לפי כיתה

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

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

@JsonTypeInfo (use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type") @JsonSubTypes ({@Type (value = Car.class, name = "car"), @Type (value = Truck.class, name = "truck")}) מחלקה מופשטת ציבורית רכב {// שדות, קונסטרוקטורים, גטרים וקובעים}

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

מחרוזת jsonDataString = mapper.writeValueAsString (serializedFleet);

הסידור מייצר את מבנה JSON הבא:

{"רכבים": [{"type": "car", "make": "Mercedes-Benz", "model": "S500", "sittingCapacity": 5, "topSpeed": 250.0}, {"type" : "משאית", "make": "Isuzu", "model": "NQR", "payloadCapacity": 7500.0}]}

מחרוזת זו משמשת ליצירת אובייקטים נתונים מחדש:

צי deserializedFleet = mapper.readValue (jsonDataString, Fleet.class);

לבסוף, כל ההתקדמות מאומתת:

assertThat (deserializedFleet.getVehicles (). get (0), instanceOf (Car.class)); assertThat (deserializedFleet.getVehicles (). get (1), instanceOf (Truck.class));

3. התעלמות ממאפיינים מסוג-על

לעיתים, יש להתעלם ממאפיינים מסוימים שעוברים בתורשה ממעמדי העל במהלך סדרת הסדרה או דה-סידור. ניתן להשיג זאת על ידי אחת משלוש שיטות: ביאורים, תערובות והתבוננות פנימית בהערות.

3.1. ביאורים

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

@JsonIgnoreProperties הוא חזק יותר מהאחר שכן הוא מאפשר לנו להתעלם ממאפיינים שעוברים בירושה מסוגי-על שאין לנו שליטה עליהם, כגון סוגים בספריה חיצונית. בנוסף, ביאור זה מאפשר לנו להתעלם ממאפיינים רבים בבת אחת, מה שעלול להוביל לקוד מובן יותר במקרים מסוימים.

מבנה הכיתה הבא משמש להדגמת השימוש בהערות:

שיעור מופשט ציבורי רכב {פרטי מיתרים פרטיים; מודל מחרוזת פרטי; רכב מוגן (מחרוזת, מודל מחרוזת) {this.make = make; this.model = מודל; } // no-arg constructor, getters and setter} @JsonIgnoreProperties ({"model", "sittingCapacity"}) מעמד מופשט ציבורי רכב מרחיב רכב {private int seatingCapacity; @Json התעלם מ- TopSpeed ​​כפול פרטי; רכב מוגן (מחרוזת, דגם מחרוזת, int seatCapacity, כפול מהיר כפול) {super (make, model); this.seatingCapacity = sittingCapacity; this.topSpeed ​​= topSpeed; } // קונסטרוקטור ללא ארג, קבצים וקובעים} מעמד ציבורי סדאן מרחיב רכב {ציבור סדאן (מחרוזת, מודל מחרוזת, int seatCapacity, כפול topSpeed) {סופר (make, model, sittingCapacity, topSpeed); } // no-arg constructor} Crossover מחלקה ציבורית מאריך רכב {גרירה כפולה פרטיתCapacity; קרוסאובר ציבורי (תווית מחרוזת, דגם מחרוזת, קיבולת מושב int, כפול topSpeed, כפול גרירה כושר) {סופר (make, דגם, sittingCapacity, topSpeed); this.towingCapacity = towingCapacity; } // קונסטרוקטור ללא ארג, גטרים וקובעים}

כפי שאתה יכול לראות, @JsonIgnore אומר לג'קסון להתעלם Car.topSpeed רכוש, ואילו @JsonIgnoreProperties מתעלם מה- רכב.דגם ו Car.seatingCapacity יחידות.

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

ממפה ObjectMapper = ObjectMapper חדש (); סדאן סדאן = סדאן חדשה ("מרצדס בנץ", "S500", 5, 250.0); קרוסאובר קרוסאובר = קרוסאובר חדש ("BMW", "X6", 5, 250.0, 6000.0); רשימת רכבים = ArrayList חדש (); כלי רכב. להוסיף (סדאן); כלי רכב. להוסיף (מוצלב); מחרוזת jsonDataString = mapper.writeValueAsString (כלי רכב);

jsonDataString מכיל את מערך JSON הבא:

[{"make": "Mercedes-Benz"}, {"make": "BMW", "towingCapacity": 6000.0}]

לבסוף, נוכיח את קיומם או היעדרם של שמות מאפיינים שונים במחרוזת JSON שהתקבלה:

assertThat (jsonDataString, containString ("make")); assertThat (jsonDataString, not (containString ("דגם"))); assertThat (jsonDataString, not (containString ("sittingCapacity"))); assertThat (jsonDataString, not (containString ("topSpeed"))); assertThat (jsonDataString, containString ("towingCapacity"));

3.2. ערבובים

תוספות מאפשרות לנו ליישם התנהגות (כגון התעלמות ממאפיינים בעת סדרת סדר ועריקת עריכה) ללא צורך להחיל הערות ישירות על כיתה. זה שימושי במיוחד כאשר מתעסקים בשיעורי צד שלישי, בהם איננו יכולים לשנות את הקוד ישירות.

סעיף משנה זה עושה שימוש חוזר בשרשרת הירושה המעמדית שהוצגה בקודמתה, אלא שה- @JsonIgnore ו @JsonIgnoreProperties ביאורים על אוטו הכיתה הוסרה:

מחלקה מופשטת ציבורית מכונית מרחיבה את הרכב {פרטיים int seatingCapacity; טופ זוגי פרטי מהיר; // שדות, בונים, גטרים וקובעים}

על מנת להדגים פעולות של תערובות, אנו נתעלם להפוך את הרכב ו Car.topSpeed ואז השתמש בבדיקה כדי לוודא שהכל עובד כצפוי.

הצעד הראשון הוא להכריז על סוג ערבוב:

מחלקה מופשטת פרטית CarMixIn {@Json התעלם ממחרוזת הציבור; @Json התעלם ממחרוזת ציבורי topSpeed; }

לאחר מכן, התערובת קשורה למחלקת נתונים דרך ObjectMapper לְהִתְנַגֵד:

ממפה ObjectMapper = ObjectMapper חדש (); mapper.addMixIn (Car.class, CarMixIn.class);

לאחר מכן אנו מייצרים אובייקטים של נתונים ומסדרים אותם למחרוזת:

סדאן סדאן = סדאן חדשה ("מרצדס בנץ", "S500", 5, 250.0); קרוסאובר קרוסאובר = קרוסאובר חדש ("BMW", "X6", 5, 250.0, 6000.0); רשימת רכבים = ArrayList חדש (); כלי רכב. להוסיף (סדאן); כלי רכב. להוסיף (מוצלב); מחרוזת jsonDataString = mapper.writeValueAsString (כלי רכב);

jsonDataString מכיל כעת את ה- JSON הבא:

[{"model": "S500", "sittingCapacity": 5}, {"model": "X6", "sittingCapacity": 5, "towingCapacity": 6000.0}]

לבסוף, בואו נוודא את התוצאה:

assertThat (jsonDataString, not (containString ("make"))); assertThat (jsonDataString, containString ("מודל")); assertThat (jsonDataString, containString ("sittingCapacity")); assertThat (jsonDataString, not (containString ("topSpeed"))); assertThat (jsonDataString, containString ("towingCapacity"));

3.3. אינטרוספקציה של ביאור

התבוננות פנימית בהערות היא השיטה החזקה ביותר להתעלם ממאפייני העל, מכיוון שהיא מאפשרת התאמה אישית מפורטת באמצעות AnnotationIntrospector.hasIgnoreMarker ממשק API.

סעיף משנה זה עושה שימוש באותה היררכיית כיתות כמו זו שקדמה לה. במקרה שימוש זה, נבקש מג'קסון להתעלם רכב.דגם, Crossover.towingCapacity וכל הנכסים המוצהרים ב אוטו מעמד. נתחיל בהכרזה על כיתה המרחיבה את ג'קסון ביאור אינטרוספקטור מִמְשָׁק:

class IgnanceIntrospector מרחיב את JacksonAnnotationIntrospector {has the Boolean hasIgnoreMarker (AnnotatedMember m)}

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

השלב הבא הוא רישום מופע של ה- בורות אינטרוספקטור כיתה עם ObjectMapper לְהִתְנַגֵד:

ממפה ObjectMapper = ObjectMapper חדש (); mapper.setAnnotationIntrospector (IgnoranceIntrospector חדש);

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

[{"make": "Mercedes-Benz"}, {"make": "BMW"}]

לבסוף אנו נוודא שהמתבונן הפנימי עבד כמתוכנן:

assertThat (jsonDataString, containString ("make")); assertThat (jsonDataString, not (containString ("דגם"))); assertThat (jsonDataString, not (containString ("sittingCapacity"))); assertThat (jsonDataString, not (containString ("topSpeed"))); assertThat (jsonDataString, not (containString ("towingCapacity")));

4. תרחישים לטיפול בתת-סוג

סעיף זה יעסוק בשני תרחישים מעניינים הרלוונטיים לטיפול בכיתות משנה.

4.1. המרה בין תת-סוגים

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

על מנת להדגים המרה מסוג לסוג אחר, נשתמש שוב ב- רכב היררכיה שנלקחה מסעיף 2, בתוספת ה- @JsonIgnore ביאור על נכסים ב אוטו ו מַשָׂאִית כדי למנוע אי התאמה.

רכב בכיתה ציבורית מאריך את הרכב {@Json התעלם ממקומות ישיבה פרטיים; @Json התעלם מ- TopSpeed ​​כפול פרטי; // בונים, גטרים וקובעים} משאית בכיתה ציבורית מרחיבה את הרכב {@ JsonIgnore private double loadCapacity; // בונים, גטרים וקובעים}

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

ממפה ObjectMapper = ObjectMapper חדש (); מכונית לרכב = מכונית חדשה ("מרצדס בנץ", "S500", 5, 250.0); משאית משאית = mapper.convertValue (רכב, Truck.class); assertEquals ("מרצדס בנץ", truck.getMake ()); assertEquals ("S500", truck.getModel ());

4.2. התפטרות ללא בונים ללא ארגומנטים

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

חלק זה ישתמש במבנה אובייקט הדומה לזה שבסעיף 2, עם שינויים מסוימים בבונים. באופן ספציפי, כל הבנאים ללא ארגז נופלים, ובונים עם תת-סוגים קונקרטיים @JsonCreator ו @JsonProperty להפוך אותם לשיטות יוצר.

רכב בכיתה ציבורית מאריך את הרכב {@JsonCreator רכב ציבורי (@JsonProperty ("make") מחרוזת make, @JsonProperty ("model") דגם מחרוזת, @JsonProperty ("ישיבה") int sittingCapacity, @JsonProperty ("topSpeed") כפול topSpeed ) {סופר (עשה, מודל); this.seatingCapacity = sittingCapacity; this.topSpeed ​​= topSpeed; } // שדות, גטרים וקובעים} משאית בכיתה ציבורית משאירה רכב {@JsonCreator משאית ציבורית (@JsonProperty ("make") מחרוזת, @JsonProperty ("model") מודל מחרוזת, @JsonProperty ("מטען") מטען כפול קיבולת {סופר (עשה, מודל); this.payloadCapacity = payloadCapacity; } // שדות, גטרים וקובעים}

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

ממפה ObjectMapper = ObjectMapper חדש (); mapper.enableDefaultTyping (); מכונית רכב = מכונית חדשה ("מרצדס בנץ", "S500", 5, 250.0); משאית משאית = משאית חדשה ("איסוזו", "NQR", 7500.0); רשימת כלי רכב = ArrayList חדש (); כלי רכב. להוסיף (רכב); כלי רכב. להוסיף (משאית); צי סידור צי = צי חדש (); serializedFleet.setVehicles (כלי רכב); מחרוזת jsonDataString = mapper.writeValueAsString (serializedFleet); mapper.readValue (jsonDataString, Fleet.class);

5. מסקנה

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

ניתן למצוא את היישום של כל הדוגמאות וקטעי הקוד בפרויקט GitHub.


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