Advanced Java Services | JDOM XPath |
Auch JDOM kennt das XPath-Konzept. Es wurde in der neuen Version JDOM2 komplett überarbeitet und ist nicht kompatibel mit der Vorgängerversion. Um mit der neuen org.jdom2.xpath.XPathFactory arbeiten zu können ist ein weiteres jar-Archiv mit dem Namen jaxen-1.1.x.jar notwendig. Dieses Archiv erhält man unter jaxen.codehaus.org/releases.html
Das Vorgehen mit JDOM2 unterscheidet sich ein wenig von Java-DOM. Sowohl org.jdom2.xpath.XPath als auch org.jdom2.xpath.jaxen.JDOMXPath sind zur Gänze deprecated. JDOM2 bietet keinen XPath mehr an, stattdessen arbeitet man nur noch mit den Klassen org.jdom2.xpath.XPathFactory und org.jdom2.xpath.XPathExpression.
Die neue XPathFactory wird realisiert durch org.jdom2.xpath.jaxen.JaxenXPathFactory. Hier braucht man also die Jaxen-Library.
Das sieht folgendermaßen aus:
import org.jdom2.xpath.XPathFactory; XPathFactory xpathFactory = XPathFactory.instance();
Einen String durch den Baum zu den gewünschten Daten bereitstellen.
String titelTextPath = "buecher/buch/titel/text()"; // Pfad zum text von <titel>
Der Pfad verwendet an seinem Ende die Funktion text(), die jede XPath-Implementierung u.a. realisieren muß. Die Funktion holt den Text des einen ChildElements von <titel>.
Feststellen, ob dieser Pfad im DOM-Baum existiert:
Die Methode compile wirft eine IllegalArgumentException falls
der Pfad nicht existiert oder ein Syntaxfehler vorliegt.
import org.jdom2.xpath.XPathExpression; XPathExpression<Object> xpathBuch = xpathFactory.compile(titelTextPath);
Wird keine Exception geworfen, kann man das Ergebnis abholen. Da es mehrere titel-Einträge gibt ist eine Liste zu erwarten. Hinweis: Ein generischer cast etwa auf XPathExpression<Parent> ist generell nicht möglich.
List<Object> buchs = xpathBuch.evaluate(doc);
evaluate() arbeitet nicht generisch, es muß gecastet werden. Im Unterschied zu Java-DOM wir hier immer eine List<Object> geliefert.
import java.io.IOException; import java.util.List; import org.jdom2.Content; import org.jdom2.Document; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; import org.jdom2.xpath.XPathExpression; import org.jdom2.xpath.XPathFactory; /** * Zugriff mit XPath von JDOM2 */ private static void xpath1b() { String xmlfile = "buecher.xml"; SAXBuilder saxBuilder = new SAXBuilder(); try { Document doc = saxBuilder.build(xmlfile); // JDOMException, IOException XPathFactory xpathFactory = XPathFactory.instance(); String titelTextPath = "buecher/buch/titel/text()"; XPathExpression<Object> expr = xpathFactory.compile(titelTextPath); List<Object> buchtitels = expr.evaluate(doc); for (int i = 0; i < buchtitels.size(); i++) { Content content = (Content) buchtitels.get(i); System.out.println(content.getValue()); } } catch (JDOMException | IOException ex) { ex.printStackTrace(); } }
Output
Deutschland, ein Wintermaerchen Der Untertan Erfolg
Mit dieser Idee läßt sich auch der komplette Baum leicht ausgeben.
String vornameTextPath = "buecher/buch/autor/vorname/text()"; // Pfad zum text von <vorname> String nachnameTextPath = "buecher/buch/autor/nachname/text()"; // Pfad zum text von <nachname> String isbnTextPath = "buecher/buch/isbn/text()"; // Pfad zum text von <isbn>
Wie oben kann man so mehrere Listen erhalten und auswerten.
Hier nochmal die Tabelle aus dem Kapitel "DOM XPath". Auch JDOM erfüllt die XPath-Specifikation.
Eine Zusammenstellung von achtzehn Pfadvarianten von einfach bis raffiniert und was man damit finden kann.
Sie beziehen sich alle auf die Datei "buecher.xml".
XPath als String | Wirkung |
---|---|
"" | TransformerException: Leerer Ausdruck. |
"/" | Liefert das Document. |
"/buecher/buch" | Liefert alle buch-Elemente (hier 3). |
"/buecher/buch[isbn]" | Liefert alle buch-Elemente die ein ChildElement namens isbn haben, hier also alle. |
"/buecher/buch/isbn" | Liefert alle isbn-Elemente. |
"/buecher/buch/isbn/text()" | text() ist eine Funktion, die das TextChild liefert. Hier wird also zu jedem isbn-Element das TextChild geliefert. Vorsicht: Textchilds können Zeilenumbrüche und Tabs enthalten, die durch die Formatierung der xml-Datei entstehen. |
"/buecher/buch/text()" | Liefert zu allen buch-Elementen alle TextChilds, hier also 12 TextChilds, macht 4 TextChilds pro buch-Element. |
"/buecher/buch/autor/text()" | Liefert zu allen autor-Elementen alle TextChilds, hier also 9 TextChilds, macht 3 TextChilds pro autor-Element. |
"/buecher/buch[@type='paperback']" | Liefert alle buch-Elemente, die das angegebene Attribut mit Namen type und dem Wert 'paperback' haben, hier also 2. |
"/buecher/buch[@type='paperback']/titel/text()" | Liefert die TextChilds aller titel-Elemente aller buch-Elemente, die das angegebene Attribut haben, hier also 2. |
"/buecher/buch/isbn[@type='13']/text()" | Liefert die Textchilds (ISBN-Nummer) aller isbn-Elemente zu allen buch-Elementen mit dem isbn-Attribut type='13'. |
"/buecher/buch[2]/titel/text()" | Liefert das TextChild zu allen titel-Elementen des zweiten buch-Elements, Zählung beginnt bei 1. |
"/buecher/buch/autor/*" | Liefert alle Elementchilds von allen autor-Elementen, liefert keine TextChilds! |
"/buecher/buch/autor/node()" | Die node()-Funktion liefert alle Childs von allen autor-Elementen, also auch die TextChilds. |
"/buecher/buch[@*]" | Liefert alle drei buch-Elemente, weil jedes buch-Element ein Atrribut hat. |
"/buecher/buch/autor[@*]" | Liefert eine leere NodeList, weil kein autor-Element (irgend)ein Attribut hat. |
"/buecher/buch/autor[vorname='Heinrich']/nachname/text()" | Liefert alle nachnamen-Elementen, die vornamen-Elemente mit dem TextChild "Heinrich" haben. |
"/buecher/buch[isbn[@type='10']]/titel/text()" | Sucht alle buch-Elemente, die ein isbn-Attribut mit type='10' haben und liefert von diesen buch-Elementen die TextChilds der titel-Elemente. |