עבודה עם צמתים של דגם עץ בג'קסון

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

מדריך זה יתמקד בעבודה עם צמתים של דגם עץ בג'קסון.

נשתמש JsonNode להמרות שונות וכן להוסיף, לשנות ולהסיר צמתים.

2. יצירת צומת

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

ממפה ObjectMapper = ObjectMapper חדש ();

מאז הקמתה של ObjectMapper האובייקט הוא יקר, מומלץ להשתמש בו לאחד לצורך פעולות מרובות.

לאחר מכן, יש לנו שלוש דרכים שונות ליצור צומת עץ ברגע שיש לנו את שלנו ObjectMapper.

2.1. בנה צומת מ- Scratch

הדרך הנפוצה ביותר ליצור צומת יש מאין היא כדלקמן:

צומת JsonNode = mapper.createObjectNode ();

לחלופין, אנו יכולים גם ליצור צומת דרך ה- JsonNodeFactory:

צומת JsonNode = JsonNodeFactory.instance.objectNode ();

2.2. לנתח ממקור JSON

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

2.3. המרה מאובייקט

ניתן להמיר צומת מאובייקט Java על ידי קריאה ל- valueToTree (אובייקט fromValue) שיטה על ObjectMapper:

צומת JsonNode = mapper.valueToTree (fromValue);

ה convertValue API מועיל גם כאן:

צומת JsonNode = mapper.convertValue (fromValue, JsonNode.class);

בואו נראה איך זה עובד בפועל. נניח שיש לנו כיתה בשם NodeBean:

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

בואו לכתוב מבחן שמוודא שההמרה מתרחשת כהלכה:

@Test הציבור בטל givenAnObject_whenConvertingIntoNode_thenCorrect () {NodeBean fromValue = חדש NodeBean (2016, "baeldung.com"); צומת JsonNode = mapper.valueToTree (fromValue); assertEquals (2016, node.get ("id"). intValue ()); assertEquals ("baeldung.com", node.get ("שם"). textValue ()); }

3. הפיכת צומת

3.1. כתוב בשם JSON

השיטה הבסיסית להפוך צומת עץ למחרוזת JSON היא הבאה:

mapper.writeValue (יעד, צומת);

היכן שהיעד יכול להיות קוֹבֶץ, OutputStream או א סוֹפֵר.

על ידי שימוש חוזר בכיתה NodeBean המוצהר בסעיף 2.3, בדיקה מוודאת ששיטה זו פועלת כצפוי:

סופי מחרוזת pathToTestFile = "node_to_json_test.json"; @ מבחן חלל ציבורי שניתן ANode_whenModifyingIt_thenCorrect () זורק IOException {String newString = "{\" nick \ ": \" cowtowncoder \ "}"; JsonNode newNode = mapper.readTree (newString); JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .set ("שם", newNode); assertFalse (rootNode.path ("שם"). נתיב ("ניק"). isMissingNode ()); assertEquals ("cowtowncoder", rootNode.path ("name"). path ("nick"). textValue ()); }

3.2. המרה לאובייקט

הדרך הנוחה ביותר להמיר JsonNode לתוך אובייקט Java הוא treeToValue ממשק API:

NodeBean toValue = mapper.treeToValue (צומת, NodeBean.class);

שווה ערך פונקציונלית ל:

NodeBean toValue = mapper.convertValue (node, NodeBean.class)

אנו יכולים לעשות זאת באמצעות זרם סמלי:

מנתח JsonParser = mapper.treeAsTokens (node); NodeBean toValue = mapper.readValue (מנתח, NodeBean.class);

לבסוף, בואו נבצע בדיקה המאמתת את תהליך ההמרה:

@Test ציבורי בטל givenANode_whenConvertingIntoAnObject_thenCorrect () זורק JsonProcessingException {JsonNode node = mapper.createObjectNode (); ((ObjectNode) צומת) .put ("id", 2016); ((ObjectNode) node) .put ("שם", "baeldung.com"); NodeBean toValue = mapper.treeToValue (צומת, NodeBean.class); assertEquals (2016, toValue.getId ()); assertEquals ("baeldung.com", toValue.getName ()); }

4. מניפולציה של צמתי עצים

רכיבי JSON הבאים, הכלולים בקובץ בשם דוגמא.json, משמשים כמבנה בסיס לפעולות הנדונות בסעיף זה לביצוע:

{"name": {"first": "Tatu", "last": "Saloranta"}, "title": "מייסד ג'קסון", "company": "FasterXML"}

קובץ JSON זה, הממוקם על שביל הכיתה, מעובד לעץ מודל:

מחלקה ציבורית ExampleStructure {ממפה ObjectMapper פרטי סטטי = ObjectMapper חדש (); סטטי JsonNode getExampleRoot () זורק IOException {InputStream exampleInput = ExampleStructure.class.getClassLoader () .getResourceAsStream ("example.json"); JsonNode rootNode = mapper.readTree (exampleInput); להחזיר rootNode; }}

שים לב כי ישמש את שורש העץ בעת המחשת פעולות בצמתים בסעיפי המשנה הבאים.

4.1. איתור צומת

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

אם הדרך לצומת ידועה מראש, זה די קל לעשות. לדוגמא, נניח שאנחנו רוצים צומת בשם אחרון, שנמצא תחת ה- שֵׁם צוֹמֶת:

JsonNode ממוקםNode = rootNode.path ("שם"). נתיב ("אחרון");

לחלופין, ה- לקבל אוֹ עם ניתן להשתמש בממשקי API במקום נָתִיב.

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

אנו יכולים לראות דוגמה לחזרה על כל הצמתים ב 5. איטרציה על פני הצמתים

4.2. הוספת צומת חדש

ניתן להוסיף צומת כילד לצומת אחר באופן הבא:

ObjectNode newNode = ((ObjectNode) locatedNode) .put (fieldName, value);

וריאנטים רבים עמוסים מדי של לָשִׂים יכול לשמש להוספת צמתים חדשים מסוגי ערך שונים.

קיימות גם שיטות רבות אחרות דומות, כולל putArray, putObject, PutPOJO, putRawValue ו putNull.

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

"address": {"city": "Seattle", "state": "Washington", "country": "United States"}

להלן המבחן המלא שעובר על כל הפעולות הללו ואימות התוצאות:

@Test ציבורי בטל givenANode_whenAddingIntoATree_thenCorrect () זורק IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); ObjectNode addedNode = ((ObjectNode) rootNode) .putObject ("כתובת"); addedNode .put ("עיר", "סיאטל") .put ("מדינה", "וושינגטון") .put ("מדינה", "ארצות הברית"); assertFalse (rootNode.path ("כתובת"). isMissingNode ()); assertEquals ("סיאטל", rootNode.path ("כתובת"). נתיב ("עיר"). textValue ()); assertEquals ("וושינגטון", rootNode.path ("כתובת"). נתיב ("מדינה"). textValue ()); assertEquals ("ארצות הברית", rootNode.path ("כתובת"). נתיב ("מדינה"). textValue ();}

4.3. עריכת צומת

An ObjectNode ניתן לשנות מופע על ידי הפעלת set (מחרוזת fieldName, ערך JsonNode) שיטה:

JsonNode locatedNode = locatedNode.set (fieldName, value);

ניתן להשיג תוצאות דומות באמצעות החלף אוֹ setAll שיטות על אובייקטים מאותו סוג.

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

@ מבחן חלל ציבורי שניתן ANode_whenModifyingIt_thenCorrect () זורק IOException {String newString = "{\" nick \ ": \" cowtowncoder \ "}"; JsonNode newNode = mapper.readTree (newString); JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .set ("שם", newNode); assertFalse (rootNode.path ("שם"). נתיב ("ניק"). isMissingNode ()); assertEquals ("cowtowncoder", rootNode.path ("name"). path ("nick"). textValue ()); }

4.4. הסרת צומת

ניתן להסיר צומת על ידי קריאה ל- הסר (מחרוזת fieldName) ממשק API בצומת האב שלו:

JsonNode removeNode = locatedNode.remove (fieldName);

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

ObjectNode locatedNode = locatedNode.remove (fieldNames);

במקרה הקיצוני כאשר אנו רוצים למחוק את כל תת הצמתים של צומת נתון ה להסיר את כל ממשק API שימושי.

המבחן הבא יתמקד בשיטה הראשונה שהוזכרה לעיל - שהיא התרחיש הנפוץ ביותר:

@Test ציבורי בטל נתון ANode_whenRemovingFromATree_thenCorrect () זורק IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); ((ObjectNode) rootNode) .remove ("חברה"); assertTrue (rootNode.path ("חברה"). isMissingNode ()); }

5. איטרציה מעל הצמתים

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

לכן, בואו להבטיח לנתוני המדגם שלנו כל שלושת הסוגים השונים על ידי הוספת מַעֲרָך:

{"name": {"first": "Tatu", "last": "Saloranta"}, "title": "מייסד ג'קסון", "company": "FasterXML", "pets": [{"type": "כלב", "מספר": 1}, {"סוג": "דג", "מספר": 50}]}

עכשיו, בואו נראה את ה- YAML שאנחנו רוצים לייצר:

שם: ראשון: Tatu אחרון: סלורנטה כותרת: חברת מייסד ג'קסון: FasterXML חיות מחמד: - סוג: מספר כלב: 1 - סוג: מספר דג: 50

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

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

5.1. בודקים את האיטרציה

נתחיל ביצירת בדיקה פשוטה שבודקת שנוכל להמיר את JSON ל- YAML.

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

@Test ציבורי בטל נתון ANodeTree_whenIteratingSubNodes_thenWeFindExpected () זורק IOException {JsonNode rootNode = ExampleStructure.getExampleRoot (); מחרוזת yaml = onTest.toYaml (rootNode); assertEquals (expectYaml, yaml); } מחרוזת ציבורית toYaml (שורש JsonNode) {StringBuilder yaml = StringBuilder חדש (); processNode (root, yaml, 0); להחזיר yaml.toString (); }}

5.2. טיפול בסוגי צומת שונים

עלינו להתמודד עם סוגים שונים של צומת באופן שונה מעט. נעשה זאת ב processNode שיטה:

תהליך ריק ריק (Nson (JsonNode jsonNode, StringBuilder yaml, int עומק) {if (jsonNode.isValueNode ()) {yaml.append (jsonNode.asText ()); } אחרת אם (jsonNode.isArray ()) {עבור (JsonNode arrayItem: jsonNode) {appendNodeToYaml (arrayItem, yaml, עומק, נכון); }} אחרת אם (jsonNode.isObject ()) {appendNodeToYaml (jsonNode, yaml, עומק, שקר); }}

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

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

למרבה הצער, הצומת עצמו אינו מכיל שום דבר שיאמר לנו זאת, ולכן נעביר דגל לתוכנו appendNodeToYaml שיטה.

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

אובייקט {"first": "Tatu", "last": "Saloranta"} Value "מייסד ג'קסון" Value "FasterXML" Array [{"type": "dog", "number": 1}, {"type": "דג", "מספר": 50}]

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

מפתח = "שם", ערך = אובייקט {"ראשון": "טאטו", "אחרון": "סלורנטה"} מפתח = "כותרת", ערך = ערך "מייסד ג'קסון" מפתח = "חברה", ערך = ערך "FasterXML "Key =" pets ", Value = Array [{" type ":" dog "," number ": 1}, {" type ":" fish "," number ": 50}]

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

חלל פרטי appendNodeToYaml (צומת JsonNode, StringBuilder yaml, int עומק, isArrayItem בוליאני) {Iterator שדות = node.fields (); בוליאני isFirst = נכון; while (fields.hasNext ()) {Entry jsonField = fields.next (); addFieldNameToYaml (yaml, jsonField.getKey (), עומק, isArrayItem && isFirst); processNode (jsonField.getValue (), yaml, עומק + 1); isFirst = שקר; }}

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

ריק ריק addFieldNameToYaml (StringBuilder yaml, String fieldName, int עומק, בוליאני isFirstInArray) {if (yaml.length ()> 0) {yaml.append ("\ n"); int requiredDepth = (isFirstInArray)? עומק -1: עומק; עבור (int i = 0; i <requiredDepth; i ++) {yaml.append (""); } אם (isFirstInArray) {yaml.append ("-"); }} yaml.append (fieldName); yaml.append (":"); }

עכשיו שיש לנו את כל הקוד כדי לחזור על הצמתים וליצור את פלט YAML, אנחנו יכולים להריץ את הבדיקה שלנו כדי להראות שזה עובד.

6. מסקנה

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

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


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