סדר סדר ורשימות של רשימה עם Gson

1. הקדמה

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

2. רשימת חפצים

מקרה נפוץ אחד הוא לסדר רשימת POJO ולהפוך אותו מחדש.

שקול את הכיתה:

מחלקה ציבורית MyClass {מזהה פרטי פרטי; שם מחרוזת פרטי; MyClass ציבורי (מזהה int, שם מחרוזת) {this.id = id; this.name = שם; } // גטרים וקובעים}

הנה איך היינו סדרתי רשימה:

@Test ציבורי בטל givenListOfMyClass_whenSerializing_thenCorrect () {List list = Arrays.asList (MyClass חדש (1, "name1"), MyClass חדש (2, "name2")); Gson gson = Gson חדש (); מחרוזת jsonString = gson.toJson (רשימה); String expectedString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2, \" name \ ": \" name2 \ "}]" ; assertEquals (expectString, jsonString); }

כפי שאנו רואים, סדרת סדר היא פשוטה למדי.

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

@Test (צפוי = ClassCastException.class) חלל ציבורי שניתן JsonString_whenIncorrectDeserializing_thenThrowClassCastException () {String inputString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2 , \ "שם \": \ "שם 2 \"}] "; Gson gson = Gson חדש (); רשימת outputList = gson.fromJson (inputString, ArrayList.class); assertEquals (1, outputList.get (0) .getId ()); }

פה, למרות שנקבל רשימה בגודל שני, לאחר עריקת-ערעור, זה לא יהיה רשימה שֶׁל הכיתה שלי. לכן, קו מס '6 זורק ClassCastException.

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

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

@ מבחן חלל ציבורי שניתן JsonString_whenDeserializing_thenReturnListOfMyClass () {String inputString = "[{\" id \ ": 1, \" name \ ": \" name1 \ "}, {\" id \ ": 2, \" name \ ": \ "name2 \"}] "; רשימת inputList = Arrays.asList (MyClass חדש (1, "name1"), MyClass חדש (2, "name2")); סוג listOfMyClassObject = סוג חדש של סוג() {} .getType (); Gson gson = Gson חדש (); רשימת outputList = gson.fromJson (inputString, listOfMyClassObject); assertEquals (inputList, outputList); }

פה, אנו משתמשים ב- Gson TypeToken כדי לקבוע את הסוג הנכון שיש לבטל את עריכתו - רשימת מערך. האידיום נהג לקבל את listOfMyClassObject מגדיר למעשה מעמד פנימי מקומי אנונימי המכיל שיטה getType () המחזירה את הסוג הפרמטר המלא.

3. רשימת אובייקטים פולימורפיים

3.1. הבעיה

שקול דוגמה להיררכיית כיתות של בעלי חיים:

שיעור מופשט ציבורי בעלי חיים {// ...} מעמד ציבורי כלב מרחיב בעלי חיים {// ...} מעמד ציבורי פרה מרחיב בעלי חיים {// ...}

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

3.2. באמצעות Deserializer מותאם אישית

אחת הדרכים לפתור זאת היא להוסיף מידע מסוג ל- JSON הסידורי. אנו מכבדים מידע מסוג זה במהלך התנערות JSON. לשם כך, עלינו לכתוב סדרת סדר וערוך שיחות מותאמים אישית משלנו.

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

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

מחלקה מופשטת ציבורית בעלי חיים {ציבור סוג מחרוזת = "בעל חיים"; }
מעמד ציבורי כלב מאריך בעלי חיים {פרטי מחרוזת petName; כלב ציבורי () {petName = "Milo"; סוג = "כלב"; } // גטרים וקובעים}
מעמד ציבורי פרה מרחיבה בעלי חיים {גזע מחרוזות פרטי; פרה ציבורית () {breed = "Jersey"; סוג = "פרה"; } // גטרים וקובעים}

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

@ מבחן חלל ציבורי נתון PolymorphicList_whenSerializeWithTypeAdapter_thenCorrect () {String expectString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Dog \ "}, {\" breed \ ": \" Jersey \ " ", \" סוג \ ": \" פרה \ "}]"; רשימת inList = ArrayList חדש (); inList.add (כלב חדש ()); inList.add (פרה חדשה ()); מחרוזת jsonString = Gson חדש (). ToJson (inList); assertEquals (expectString, jsonString); }

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

מחלקה ציבורית AnimalDeserializer מיישם את JsonDeserializer {private String animalTypeElementName; Gson gson פרטי; מפה פרטית animalTypeRegistry; AnimalDeserializer ציבורי (מחרוזת animalTypeElementName) {this.animalTypeElementName = animalTypeElementName; this.gson = Gson חדש (); this.animalTypeRegistry = HashMap חדש (); } public void registerBarnType (String animalTypeName, Class animalType) {animalTypeRegistry.put (animalTypeName, animalType); } deserialize ציבורי של בעלי חיים (JsonElement json, Type typeOfT, JsonDeserializationContext context) {JsonObject animalObject = json.getAsJsonObject (); JsonElement animalTypeElement = animalObject.get (animalTypeElementName); Class animalType = animalTypeRegistry.get (animalTypeElement.getAsString ()); החזר gson.fromJson (animalObject, animalType); }}

הנה ה animalTypeRegistry המפה שומרת על המיפוי בין שם הכיתה לסוג הכיתה.

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

בואו נראה כיצד להשתמש ב- deserializer המותאם אישית שלנו:

@ מבחן חלל ציבורי נתון PolymorphicList_whenDeserializeWithTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Dog \ "}, {\" breed \ ": \" Jersey \ " ", \" סוג \ ": \" פרה \ "}]"; AnimalDeserializer deserializer = AnimalDeserializer חדש ("סוג"); deserializer.registerBarnType ("כלב", כלב.קלאס); deserializer.registerBarnType ("פרה", פרה.קלאס); Gson gson = GsonBuilder חדש () .registerTypeAdapter (Animal.class, deserializer) .create (); רשימת outList = gson.fromJson (inputString, TypeToken חדש() {}. getType ()); assertEquals (2, outList.size ()); assertTrue (outList.get (0) מופע של כלב); assertTrue (outList.get (1) מופע של פרה); }

3.3. באמצעות RuntimeTypeAdapterFactory

חלופה לכתיבת deserializer מותאם אישית היא להשתמש ב- RuntimeTypeAdapterFactory בכיתה בקוד המקור של Gson. למרות זאת, הספרייה לא נחשפת לשימוש המשתמש. לפיכך, נצטרך ליצור עותק של הכיתה בפרויקט Java שלנו.

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

@ מבחן חלל ציבורי givenPolymorphicList_whenDeserializeWithRuntimeTypeAdapter_thenCorrect () {String inputString = "[{\" petName \ ": \" Milo \ ", \" type \ ": \" Dog \ "}, {\" breed \ ": \" Jersey \ ", \" סוג \ ": \" פרה \ "}]"; סוג listOfAnimals = סוג חדש של סוג() {}. getType (); RuntimeTypeAdapterFactory מתאם = RuntimeTypeAdapterFactory.of (Animal.class, "type") .registerSubtype (Dog.class) .registerSubtype (Cow.class); Gson gson = GsonBuilder חדש (). RegisterTypeAdapterFactory (מתאם) .create (); List outList = gson.fromJson (inputString, listOfAnimals); assertEquals (2, outList.size ()); assertTrue (outList.get (0) מופע של כלב); assertTrue (outList.get (1) מופע של פרה); }

שים לב שהמנגנון הבסיסי עדיין זהה.

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

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

4. מסקנה

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

כרגיל, הקוד זמין ב- GitHub.


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