Advanced Java Services | DOM XPath |
Durch das in Java 1.5 eingeführte Package javax.xml.xpath wird der Zugriff auf den DOM-Baum drastisch vereinfacht. Da eine XML-Datei den gleichen Regeln folgt wie ein Dateisystem liegt die Einführung eines Pfadkonzepts nahe. Wir vereinfachen unser letztes Beispiel mit Hilfe der Möglichkeiten, die Java 1.5 bereitgestellt hat. Mit einem XPath kann man z. Bsp. direkt auf die Daten zugreifen.
Anlegen einer Instanz von XPath
import javax.xml.xpath.XPath; import javax.xml.xpath.XPathFactory; XPathFactory xPathFactory = XPathFactory.newInstance(); XPath xPath = xPathFactory.newXPath();
Einen XPath 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>. Siehe auch die unten folgende Tabelle.
Den Pfad übergibt man der Methode compile. Diese Methode liefert eine Instanz vom Typ XPathExpression. Falls der übergebene Pfad nicht existiert oder ein Syntaxfehler vorliegt wird eine XPathExpressionException geworfen.
import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; XPathExpression xPathExpression = xPath.compile(titelTextPath); // XPathExpressionException
Wird keine Exception geworfen, kann man das Ergebnis abholen. Da es mehrere titel-Einträge gibt ist eine Liste zu erwarten. Für diesen Fall gibt es die Konstante XPathConstants.NODESET, die wie folgt eingesetzt wird:
NodeList result = (NodeList)expr.evaluate(doc, XPathConstants.NODESET);
evaluate() arbeitet nicht generisch, es muß gecastet werden.
import java.io.File; import java.io.IOException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; /** * Titel ausgeben mit XPath */ private static void beispiel1() { try { DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); // ParserConfigurationException File xmlFile = new File("buecher.xml"); Document doc = dBuilder.parse(xmlFile); // SAXException, IOException doc.getDocumentElement().normalize(); // com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl XPathFactory xPathFactory = XPathFactory.newInstance(); XPath xPath = xPathFactory.newXPath(); String titelTextPath = "buecher/buch/titel/text()"; // Pfad zum text von// Liefert eine NodeList von TextNodes XPathExpression expr = xPath.compile(titelTextPath); // XPathExpressionException NodeList result = (NodeList)expr.evaluate(doc, XPathConstants.NODESET); //Object result = expr.evaluate(doc, XPathConstants.NODESET); NodeList titelTextNodes = (NodeList) result; // com.sun.org.apache.xml.internal.dtm.ref.DTMNodeList for (int i = 0; i < titelTextNodes.getLength(); i++) { Node node = titelTextNodes.item(i); System.out.println(node.getNodeValue()); } } catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException 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 org.w3c.dom.NodeList erhalten und auswerten.
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. |