עבודה עם XML ב- Groovy

1. הקדמה

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

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

2. הגדרת המודל

בואו נגדיר מבנה XML במדריך המשאבים שלנו בו נשתמש בכל הדוגמאות שלנו:

  הצעדים הראשונים בג'אווה סיינה קר 2018-01-01 דוקר את יישום SpringBoot שלך ג'ונאס לוגו 2018-12-01 מדריך SpringBoot דניאל פרגוסון 12-06 2018 Java 12 תובנות סיינה קר 2018-07-22 

וקרא את זה לתוך InputStream מִשְׁתַנֶה:

def xmlFile = getClass (). getResourceAsStream ("articles.xml")

3. XmlParser

נתחיל לחקור את הזרם הזה עם ה- XmlParser מעמד.

3.1. קריאה

קריאה וניתוח של קובץ XML היא ככל הנראה פעולת ה- XML ​​הנפוצה ביותר שיהיה על מפתח לעשות. ה XmlParser מספק ממשק פשוט מאוד המיועד בדיוק לזה:

מאמרים def = XmlParser חדש (). ניתוח (xmlFile)

בשלב זה אנו יכולים לגשת לתכונות ולערכים של מבנה XML באמצעות ביטויי GPath.

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

def "צריך לקרוא את קובץ ה- XML ​​כמו שצריך" () {נתון: "קובץ ה- XML" כאשר: "שימוש ב- XmlParser לקריאת הקובץ" def articles = XmlParser חדש (). parse (xmlFile) ואז: "Xml נטען כהלכה" מאמרים. '* '.size () == 4 articles.article [0] .author.firstname.text () == "סיינה" מאמרים. Article [2].' date-release'.text () == "2018-06- 12 "articles.article [3] .title.text () ==" Java 12 insights "articles.article.find {it.author. '@ Id'.text () ==" 3 "} .author.firstname. טקסט () == "Daniele"}

כדי להבין כיצד לגשת לערכי XML וכיצד להשתמש בביטויי GPath, נתמקד לרגע במבנה הפנימי של התוצאה של XmlParser # ניתוח מבצע.

ה מאמרים אובייקט הוא מופע של groovy.util.Node. כֹּל צוֹמֶת מורכב משם, מפת תכונות, ערך והורה (שיכולים להיות שניהם ריק או אחר צוֹמֶת).

במקרה שלנו, הערך של מאמרים הוא groovy.util.NodeList מופע, שהוא כיתת עטיפה לאוסף של צוֹמֶתס. ה NodeList מרחיב את java.util.ArrayList class, המספק חילוץ של אלמנטים לפי אינדקס. כדי להשיג ערך מחרוזת של a צוֹמֶת, אנו משתמשים groovy.util.Node # טקסט ().

בדוגמה שלעיל הצגנו כמה ביטויים של GPath:

  • articles.article [0] .author.firstname - קבל את שמו הפרטי של המחבר עבור המאמר הראשון - מאמרים. מאמר [n] היה ניגש ישירות ל- נהמאמר
  • ‘*' - קבל רשימה של מאמרהילדים - זה המקבילה ל groovy.util.Node # ילדים ()
  • מחבר.'@id ' - להשיג את מְחַבֵּר אלמנטים תְעוּדַת זֶהוּת תכונה - מחבר.'@attributeName ' ניגש לערך התכונה בשמו (המקבילות הן: מחבר ['@ id'] ו [מוגן בדוא"ל])

3.2. הוספת צומת

בדומה לדוגמא הקודמת, בואו נקרא תחילה את תוכן ה- XML ​​למשתנה. זה יאפשר לנו להגדיר צומת חדש ולהוסיף אותו לרשימת המאמרים שלנו באמצעות groovy.util.Node # נספח.

בואו כעת נבצע מבחן שמוכיח את נקודתנו:

def "צריך להוסיף צומת ל- xml קיים באמצעות NodeBuilder" () {נתון: "אובייקט XML" def מאמרים = XmlParser חדש (). parse (xmlFile) כאשר: "הוספת צומת ל- xml" def articleNode = חדש NodeBuilder (). מאמר ( id: '5') {title ('חוצה XML בקצרה') מחבר {firstname ('Martin') lastname ('Schmidt')} 'date-release' ('2019-05-18')} articles.append (articleNode) ואז: "מאמרים מתווספים ל- XML ​​כראוי" מאמרים. '*'. גודל () == 5 מאמרים.

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

בואו נשים לב גם שהשתמשנו groovy.util.NodeBuilder, המהווה אלטרנטיבה מסודרת לשימוש ב- צוֹמֶת בונה עבורנו צוֹמֶת הַגדָרָה.

3.3. שינוי צומת

אנו יכולים גם לשנות את ערכי הצמתים באמצעות ה- XmlParser. לשם כך, בוא וננתח שוב את התוכן של קובץ ה- XML. לאחר מכן נוכל לערוך את צומת התוכן על ידי שינוי ה- ערך שדה של צוֹמֶת לְהִתְנַגֵד.

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

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

def "צריך לשנות את הצומת" () {נתון: "אובייקט XML" def מאמרים = XmlParser חדש (). ניתוח (xmlFile) כאשר: "שינוי ערך של אחד הצמתים" articles.article.each {it.'release-date '[0] .value = "18.05.2019"} ואז: "XML מתעדכן" articles.article.findAll {it.'release-date'.text ()! = "18.05.2019"}. זה ריק() }

בדוגמה שלעיל השתמשנו גם ב- API של Groovy Collections כדי לחצות את ה- NodeList.

3.4. החלפת צומת

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

באופן דומה להוספת אלמנט חדש, נשתמש ב- NodeBuilder בשביל ה צוֹמֶת הגדרה ואז החלף את אחד הצמתים הקיימים בתוכו באמצעות groovy.util.Node # החלף את הצומת:

def "צריך להחליף את הצומת" () {נתון: "אובייקט XML" def מאמרים = XmlParser חדש (). מנתח (xmlFile) כאשר: "הוספת צומת ל- xml" def articleNode = חדש NodeBuilder (). מאמר (id: '5' ) {title ('חוצה XML בקצרה') מחבר {firstname ('Martin') שם משפחה ('Schmidt')} 'date-release' ('2019-05-18')} articles.article [0] .replaceNode (articleNode) ואז: "מאמרים מתווספים ל- Xml כראוי" מאמרים. '*'. גודל () == 4 מאמרים.

3.5. מחיקת צומת

מחיקת צומת באמצעות ה- XmlParser הוא די מסובך. למרות ש צוֹמֶת הכיתה מספקת את להסיר (ילד הצומת) שיטה, ברוב המקרים לא היינו משתמשים בה מעצמה.

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

כברירת מחדל, גישה לאלמנטים המקוננים באמצעות שרשרת של Node.NodeList הפניות מחזיר עותק של צמתי הילדים המקבילים. בגלל זה, אנחנו לא יכולים להשתמש ב- java.util.NodeList # removeAll שיטה ישירות על שלנו מאמר אוסף.

כדי למחוק צומת על ידי פרדיקט, עלינו למצוא תחילה את כל הצמתים התואמים את מצבנו, ואז לחזור דרכם ולהפעיל java.util.Node # הסר שיטה על ההורה בכל פעם.

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

def "אמור להסיר מאמר מ- xml" () {נתון: "אובייקט XML" def מאמרים = XmlParser חדש (). parse (xmlFile) כאשר: "הסרת כל המאמרים מלבד אלה עם id == 3" מאמרים. Article .findAll { it.author. '@ id'.text ()! = "3"}. כל {articles.remove (it)} ואז: "נותר רק מאמר אחד" articles.children (). size () == 1 articles.article [0] .author. '@ id'.text () == "3"}

כפי שאנו רואים, כתוצאה מפעולת ההסרה שלנו, קיבלנו מבנה XML עם מאמר אחד בלבד, וזהו 3.

4. XmlSlurper

Groovy מספקת גם שיעור נוסף המוקדש לעבודה עם XML. בחלק זה נראה כיצד לקרוא ולתפעל את מבנה ה- XML ​​באמצעות ה- XmlSlurper.

4.1. קריאה

כמו בדוגמאות הקודמות שלנו, נתחיל בניתוח מבנה ה- XML ​​מקובץ:

def "צריך לקרוא את קובץ ה- XML ​​כמו שצריך" () {נתון: "קובץ ה- XML" כאשר: "שימוש ב- XmlSlurper לקריאת הקובץ" def articles = XmlSlurper חדש (). parse (xmlFile) ואז: "Xml נטען כהלכה" מאמרים. '* '. size () == 4 articles.article [0] .author.firstname == "סיינה" articles.article [2].' תאריך שחרור '== "2018-06-12" articles.article [3] .title == "Java 12 insights" articles.article.find {it.author.'@id '== "3"} .author.firstname == "Daniele"}

כפי שאנו רואים, הממשק זהה לזה של XmlParser. עם זאת, מבנה הפלט משתמש ב- groovy.util.slurpersupport.GPathResult, שהוא שיעור עטיפה עבור צוֹמֶת. GPathResult מספק הגדרות פשוטות לשיטות כגון: שווים() ו toString () על ידי עטיפה צומת # טקסט (). כתוצאה מכך אנו יכולים לקרוא שדות ופרמטרים ישירות בעזרת שמותיהם בלבד.

4.2. הוספת צומת

הוספת א צוֹמֶת דומה מאוד גם לשימוש XmlParser. אולם במקרה זה, groovy.util.slurpersupport.GPathResult # appendNode מספק שיטה שלוקחת מופע של java.lang. אובייקט כוויכוח. כתוצאה מכך, אנו יכולים לפשט חדש צוֹמֶת הגדרות בעקבות אותה מוסכמה שהונהגה על ידי צוֹמֶתבּוֹנֶה:

def "צריך להוסיף צומת ל- xml קיים" () {נתון: "אובייקט XML" def מאמרים = XmlSlurper חדש (). parse (xmlFile) כאשר: "הוספת צומת ל- xml" articles.appendNode {מאמר (id: '5') {title ('חוצה XML בקצרה') מחבר {firstname ('Martin') שם משפחה ('Schmidt')} 'release date' ('2019-05-18')}} מאמרים = XmlSlurper חדש (). parseText (XmlUtil.serialize (מאמרים)) ואז: "הצומת מתווסף ל- XML ​​כראוי" מאמרים. '*'. גודל () == 5 מאמרים. מאמר [4] .title == "מעבר XML בקליפת האגוזים"}

במקרה שעלינו לשנות את מבנה ה- XML ​​שלנו עם XmlSlurper, עלינו לאתחל מחדש את שלנו מאמרים התנגד לראות את התוצאות. אנו יכולים להשיג זאת באמצעות השילוב של ה- groovy.util.XmlSlurper # parseText וה groovy.xmlXmlUtil # סידורי שיטות.

4.3. שינוי צומת

כפי שהזכרנו קודם, GPathResult מציג גישה פשוטה למניפולציות נתונים. עם זאת, בניגוד ל XmlSlurper, אנו יכולים לשנות את הערכים ישירות באמצעות שם הצומת או שם הפרמטר:

def "צריך לשנות צומת" () {נתון: "אובייקט XML" def מאמרים = XmlSlurper חדש (). לנתח (xmlFile) כאשר: "שינוי ערך של אחד הצמתים" articles.article.each {it.'-date-release '= "2019-05-18"} ואז: "XML עודכן" articles.article.findAll {it.'release-date'! = "18-05-2019" .isEmpty ()}

נבחין שכאשר אנו משנים רק את הערכים של אובייקט ה- XML, איננו צריכים לנתח את כל המבנה שוב.

4.4. החלפת צומת

עכשיו נעבור להחלפת הצומת כולו. שוב, ה GPathResult מגיע להצלה. אנחנו יכולים להחליף את הצומת בקלות באמצעות groovy.util.slurpersupport.NodeChild # החלף את הצומת, שמתארך GPathResult ועוקב אחר אותה מוסכמה של שימוש ב- לְהִתְנַגֵד ערכים כטיעונים:

def "צריך להחליף צומת" () {נתון: "אובייקט XML" def מאמרים = XmlSlurper חדש (). לנתח (xmlFile) כאשר: "החלפת מאמרים של צומת". Article [0] .replaceNode {מאמר (id: '5') {title ('חוצה XML בקצרה') מחבר {firstname ('Martin') שם משפחה ('Schmidt')} 'release date' ('2019-05-18')}} מאמרים = XmlSlurper חדש (). parseText (XmlUtil.serialize (מאמרים)) ואז: "הצומת מוחלף כראוי" מאמרים. '*'. גודל () == 4 מאמרים. מאמר [0] .title == "חוצה XML בקליפת האגוזים"}

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

4.5. מחיקת צומת

כדי להסיר צומת באמצעות XmlSlurper, אנחנו יכולים לעשות שימוש חוזר ב groovy.util.slurpersupport.NodeChild # החלף את הצומת שיטה פשוט על ידי מתן ריק צוֹמֶת הַגדָרָה:

def "צריך להסיר מאמר מ- xml" () {נתון: "אובייקט XML" def מאמרים = XmlSlurper חדש (). parse (xmlFile) כאשר: "מסיר את כל המאמרים למעט אלה עם id == 3" מאמרים. Article .findAll { it.author.'@id '! = "3"} .replaceNode {} מאמרים = XmlSlurper חדש (). parseText (XmlUtil.serialize (מאמרים)) ואז: "נותר רק מאמר אחד" articles.children (). גודל () == 1 מאמרים. מאמר [0] .מחבר. '@ id' == "3"}

שוב, שינוי מבנה ה- XML ​​דורש אתחול מחדש של שלנו מאמרים לְהִתְנַגֵד.

5. XmlParser לעומת XmlSlurper

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

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

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

ההחלטה באיזה כלי להשתמש צריכה להיעשות בזהירות ותלויה לחלוטין במקרה השימוש.

6. MarkupBuilder

מלבד קריאה ומניפולציה של עץ ה- XML, גרובי מספק גם כלים ליצירת מסמך XML מאפס. בואו ניצור כעת מסמך המורכב משני המאמרים הראשונים מהדוגמה הראשונה שלנו באמצעות groovy.xml.MarkupBuilder:

def "צריך ליצור XML כראוי" () {נתון: "מבני צומת" כאשר: "שימוש ב- MarkupBuilderTest ליצירת מבנה XML" def writer = new StringWriter () MarkupBuilder חדש (כותב). מאמרים {מאמר {title ('השלבים הראשונים ב- Java ') author (id:' 1 ') {firstname (' Siena ') lastname (' Kerr ')}' date release '(' 2018-12-01 ')} article {title (' Dockerize your SpringBoot ') מחבר (id: '2') {firstname ('Jonas') שם משפחה ('Lugo')} 'תאריך שחרור' ('2018-12-01')}} ואז: "Xml נוצר כהלכה" XmlUtil.serialize ( writer.toString ()) == XmlUtil.serialize (xmlFile.text)}

בדוגמה שלעיל אנו יכולים לראות זאת MarkupBuilder משתמש באותה גישה עבור ה- צוֹמֶת הגדרות בהן השתמשנו NodeBuilder ו GPathResult קוֹדֶם.

להשוואת תפוקה מ MarkupBuilder עם מבנה ה- XML ​​הצפוי, השתמשנו ב- groovy.xml.XmlUtil # סידור שיטה.

7. מסקנה

במאמר זה, בדקנו דרכים מרובות לתפעל מבני XML באמצעות Groovy.

בדקנו דוגמאות לניתוח, הוספה, עריכה, החלפה ומחיקה של צמתים באמצעות שני שיעורים שמספק גרובי: XmlParser ו XmlSlurper. דנו גם בהבדלים ביניהם והראינו כיצד נוכל לבנות עץ XML מאפס באמצעות MarkupBuilder.

כמו תמיד, הקוד השלם המשמש במאמר זה זמין באתר GitHub.


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