מבוא ל- XPath עם Java

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

במאמר זה נעבור את היסודות של XPath עם התמיכה ב- Java JDK הסטנדרטי.

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

XPath הוא תחביר סטנדרטי המומלץ על ידי W3C, זהו סט ביטויים לנווט במסמכי XML. תוכל למצוא הפניה מלאה ל- XPath כאן.

2. מנתח XPath פשוט

ייבא javax.xml.namespace.NamespaceContext; ייבא javax.xml.parsers.DocumentBuilder; ייבא javax.xml.parsers.DocumentBuilderFactory; ייבא javax.xml.parsers.ParserConfigurationException; ייבא javax.xml.xpath.XPath; ייבא javax.xml.xpath.XPathConstants; ייבא javax.xml.xpath.XPathExpressionException; ייבא javax.xml.xpath.XPathFactory; ייבא org.w3c.dom.Document; מחלקה ציבורית DefaultParser {קובץ קובץ פרטי; DefaultParser ציבורי (קובץ קובץ) {this.file = file; }} 

עכשיו בואו נסתכל מקרוב על האלמנטים שתמצאו ב- DefaultParser:

FileInputStream fileIS = FileInputStream חדש (this.getFile ()); DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance (); בונה DocumentBuilder = builderFactory.newDocumentBuilder (); מסמך xmlDocument = builder.parse (fileIS); XPath xPath = XPathFactory.newInstance (). NewXPath (); ביטוי מחרוזת = "/ הדרכות / הדרכה"; nodeList = (NodeList) xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODESET);

בואו נשבור את זה:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance ();

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

בונה DocumentBuilder = builderFactory.newDocumentBuilder ();

לאחר מופע של מחלקה זו, אנו יכולים לנתח מסמכי XML ממקורות קלט שונים רבים כמו InputStream, קוֹבֶץ, כתובת אתר ו SAX:

מסמך xmlDocument = builder.parse (fileIS);

א מסמך (org.w3c.dom.Document) מייצג את כל מסמך ה- XML, הוא השורש של עץ המסמך, מספק את הגישה הראשונה שלנו לנתונים:

XPath xPath = XPathFactory.newInstance (). NewXPath ();

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

xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODESET);

אנו יכולים לקמפל ביטוי XPath שהועבר כמחרוזת ולהגדיר איזה סוג נתונים אנו מצפים לקבל כזה NODESET, צוֹמֶת אוֹ חוּט לדוגמה.

3. בואו נתחיל

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

   מבוא לגויאבה לגויאבה 04/04/2016 GuavaAuthor XML מבוא ל- XPath 04/05/2016 XMLAuthor 

3.1. אחזר רשימת אלמנטים בסיסית

השיטה הראשונה היא שימוש פשוט בביטוי XPath לאחזור רשימת צמתים מ- XML:

FileInputStream fileIS = FileInputStream חדש (this.getFile ()); DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance (); בונה DocumentBuilder = builderFactory.newDocumentBuilder (); מסמך xmlDocument = builder.parse (fileIS); XPath xPath = XPathFactory.newInstance (). NewXPath (); ביטוי מחרוזת = "/ הדרכות / הדרכה"; nodeList = (NodeList) xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODESET); 

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

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

3.2. אחזור צומת ספציפי לפי המזהה שלו

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

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance (); בונה DocumentBuilder = builderFactory.newDocumentBuilder (); מסמך xmlDocument = builder.parse (this.getFile ()); XPath xPath = XPathFactory.newInstance (). NewXPath (); ביטוי מחרוזת = "/ הדרכות / הדרכה [@ tutId =" + "'" + id + "'" + "]"; node = (Node) xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODE); 

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

/ הדרכות / הדרכות [1]

/ הדרכות / הדרכות [first ()]

/ הדרכות / הדרכה [עמדה () <4]

תוכל למצוא התייחסות מלאה לפרדיקטים כאן

3.3. אחזור צמתים לפי שם תג ספציפי

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

מסמך xmlDocument = builder.parse (this.getFile ()); this.clean (xmlDocument); XPath xPath = XPathFactory.newInstance (). NewXPath (); ביטוי מחרוזת = "// הדרכה [צאצא :: כותרת [טקסט () =" + "'" + שם + "'" + "]]"; nodeList = (NodeList) xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODESET); 

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

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

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

3.4. מניפולציה של נתונים בביטויים

XPath מאפשר לנו לתפעל נתונים גם בביטויים במידת הצורך.

XPath xPath = XPathFactory.newInstance (). NewXPath (); ביטוי מחרוזת = "// הדרכה [מספר (לתרגם (תאריך, '/', ''))>" + תאריך + "]"; nodeList = (NodeList) xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODESET); 

בביטוי זה אנו מעבירים לשיטה שלנו מחרוזת פשוטה כתאריך שנראה כמו "ddmmyyyy" אך ה- XML ​​שומר את הנתונים הללו בפורמט "dd / mm / yyyy"אז כדי להתאים לתוצאה אנו מבצעים מניפולציה במחרוזת כדי להמיר אותה לפורמט הנתונים הנכון המשמש את המסמך שלנו ואנו עושים זאת באמצעות אחת מהפונקציות המסופקות על ידי XPath

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

אם למסמך ה- XML ​​שלנו יש מרחב שמות המוגדר כפי שהוא נמצא ב- example_namespace.xml המשמש כאן, הכללים לאחזור הנתונים הדרושים לנו ישתנו מכיוון ש- XML ​​שלנו מתחיל כך:

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

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

קודם כל עלינו להגדיר את ההקשר של מרחב השמות כדי ש- XPath יוכל לדעת היכן אנו מחפשים את הנתונים שלנו:

xPath.setNamespaceContext (חדש NamespaceContext () {@Override ציבור Iterator getPrefixes (מחרוזת arg0) {להחזיר null;} @Override ציבור מחרוזת getPrefix (מחרוזת arg0) {להחזיר null;} @Override מחרוזת getNamespaceURI (מחרוזת arg0) {אם (" bdn ".equals (arg0)) {return" / full_archive ";} return null;}}); 

בשיטה לעיל אנו מגדירים "bdn"כשם מרחב השמות שלנו"/ ארכיון מלא", ומעתה עלינו להוסיף"bdn"לביטויי XPath המשמשים לאיתור אלמנטים:

ביטוי מחרוזת = "/ bdn: Tutorials / bdn: Tutorial"; nodeList = (NodeList) xPath.compile (expression) .evaluate (xmlDocument, XPathConstants.NODESET); 

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

3.6. הימנעות מבעיות בצמתים ריקים

כפי שניתן לשים לב, בקוד בסעיף 3.3 במאמר זה נקראת פונקציה חדשה בדיוק לאחר ניתוח ה- XML ​​שלנו לאובייקט מסמך, .clean זה (xmlDocument);

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

התקשרנו node .getFirstChild () כשאנחנו חוזרים על הכל אלמנטים המחפשים את מידע, אך במקום מה שאנחנו מחפשים פשוט יש לנו את "# טקסט" כצומת ריק.

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

ChildList childs = node.getChildNodes (); עבור (int n = childs.getLength () - 1; n> = 0; n--) {ילד הצומת = childs.item (n); nodeType קצר = child.getNodeType (); אם (nodeType == Node.ELEMENT_NODE) ​​{נקי (ילד); } אחר אם (nodeType == Node.TEXT_NODE) ​​{String trimmedNodeVal = child.getNodeValue (). trim (); אם (trimmedNodeVal.length () == 0) {node.removeChild (ילד); } אחר {child.setNodeValue (trimmedNodeVal); }} אחרת אם (nodeType == Node.COMMENT_NODE) ​​{node.removeChild (ילד); }}

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

4. מסקנות

כאן רק הצגנו את תמיכת ברירת המחדל של XPath, אבל יש עכשיו ספריות פופולריות רבות כמו JDOM, Saxon, XQuery, JAXP, Jaxen או אפילו ג'קסון. ישנן ספריות לניתוח HTML ספציפי כמו JSoup.

זה לא מוגבל ל- Java, ביטויי XPath יכולים לשמש בשפת XSLT כדי לנווט במסמכי XML.

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

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