Einführung in XPath mit Java

Einführung in XPath mit Java

1. Überblick

In diesem Artikel gehen wir überthe basics of XPath with the support in the standard Java JDK.

Wir werden ein einfaches XML-Dokument verwenden, es verarbeiten und sehen, wie wir das Dokument durchgehen, um die benötigten Informationen daraus zu extrahieren.

XPath ist eine Standardsyntax, die vom W3C empfohlen wird. Es handelt sich um eine Reihe von Ausdrücken zum Navigieren in XML-Dokumenten. Sie finden eine vollständige XPath-Referenzhere.

2. Ein einfacher XPath-Parser

import javax.xml.namespace.NamespaceContext;
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.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;

public class DefaultParser {

    private File file;

    public DefaultParser(File file) {
        this.file = file;
    }
}

Schauen wir uns nun die Elemente genauer an, die Sie inDefaultParser finden:

FileInputStream fileIS = new FileInputStream(this.getFile());
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(fileIS);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Tutorials/Tutorial";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

Lassen Sie uns das zusammenfassen:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

Wir werden dieses Objekt verwenden, um einen DOM-Objektbaum aus unserem XML-Dokument zu erstellen:

DocumentBuilder builder = builderFactory.newDocumentBuilder();

Mit einer Instanz dieser Klasse können wir XML-Dokumente aus vielen verschiedenen Eingabequellen wieInputStream,File,URL undSAX analysieren:

Document xmlDocument = builder.parse(fileIS);

EinDocument (org.w3c.dom.Document) repräsentiert das gesamte XML-Dokument, ist das Stammverzeichnis des Dokumentbaums und bietet unseren ersten Zugriff auf Daten:

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

Über das XPath-Objekt greifen wir auf die Ausdrücke zu und führen sie über unser Dokument aus, um das zu extrahieren, was wir benötigen:

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

Wir können einen XPath-Ausdruck kompilieren, der als Zeichenfolge übergeben wird, und definieren, welche Art von Daten wir beispielsweise fürNODESET,NODE oderString erwarten.

3. Lasst uns beginnen

Nachdem wir uns nun die Basiskomponenten angesehen haben, die wir verwenden werden, beginnen wir zu Testzwecken mit einigem Code, der einfaches XML verwendet:



    
        Guava
  Introduction to Guava
  04/04/2016
  GuavaAuthor
    
    
        XML
  Introduction to XPath
  04/05/2016
  XMLAuthor
    

3.1. Rufen Sie eine grundlegende Liste von Elementen ab

Die erste Methode ist die einfache Verwendung eines XPath-Ausdrucks zum Abrufen einer Liste von Knoten aus dem XML:

FileInputStream fileIS = new FileInputStream(this.getFile());
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(fileIS);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Tutorials/Tutorial";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

Wir können die im Stammknoten enthaltene Tutorial-Liste mit dem obigen Ausdruck oder mit dem Ausdruck "//Tutorial" abrufen, aber dieser wird alle<Tutorial>-Knoten im Dokument vom aktuellen Knoten abrufen, unabhängig davon Wo sie sich im Dokument befinden, bedeutet dies, dass auf jeder Ebene des Baums ab dem aktuellen Knoten.

DasNodeList, das durch Angabe vonNODESET an die Kompilierungsanweisung als Rückgabetyp zurückgegeben wird, ist eine geordnete Sammlung von Knoten, auf die zugegriffen werden kann, indem ein Index als Parameter übergeben wird.

3.2. Abrufen eines bestimmten Knotens anhand seiner ID

Wir können ein Element basierend auf einer bestimmten ID suchen, indem wir einfach filtern:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(this.getFile());
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/Tutorials/Tutorial[@tutId=" + "'" + id + "'" + "]";
node = (Node) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODE);

Mithilfe dieser Art von Ausdrücken können wir nach jedem Element filtern, nach dem wir suchen müssen, indem wir einfach die richtige Syntax verwenden. Diese Art von Ausdrücken werden Prädikate genannt und bieten eine einfache Möglichkeit, bestimmte Daten in einem Dokument zu finden. Beispiel:

/Tutorials/Tutorial[1]

/Tutorials/Tutorial[first()]

/Tutorials/Tutorial[position()<4]

Sie finden eine vollständige Referenz der Prädikatehere

3.3. Abrufen von Knoten anhand eines bestimmten Tag-Namens

Jetzt gehen wir weiter, indem wir Achsen einführen. Lassen Sie uns sehen, wie dies funktioniert, indem Sie es in einem XPath-Ausdruck verwenden:

Document xmlDocument = builder.parse(this.getFile());
this.clean(xmlDocument);
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "//Tutorial[descendant::title[text()=" + "'" + name + "'" + "]]";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

Mit dem oben verwendeten Ausdruck suchen wir nach jedem<Tutorial>-Element, das einen Nachkommen<title> hat, wobei der Text als Parameter in der Variablen "name" übergeben wird.

Nach der für diesen Artikel bereitgestellten Beispiel-XML könnten wir nach<title> suchen, die den Text "Guava" oder "XML" enthalten, und wir werden das gesamte<Tutorial>-Element mit all seinen Daten abrufen.

Achsen bieten eine sehr flexible Möglichkeit zum Navigieren in einem XML-Dokument. Eine vollständige Dokumentation finden Sie inofficial site.

3.4. Bearbeiten von Daten in Ausdrücken

Mit XPath können wir bei Bedarf auch Daten in den Ausdrücken bearbeiten.

XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "//Tutorial[number(translate(date, '/', '')) > " + date + "]";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

In diesem Ausdruck übergeben wir unserer Methode eine einfache Zeichenfolge als Datum, das wie "TTMJJJJ" aussieht, aber das XML speichert diese Daten im Format "dd/mm/yyyy". Um einem Ergebnis zu entsprechen, bearbeiten wir die Zeichenfolge, um sie zu konvertieren auf das korrekte Datenformat, das von unserem Dokument verwendet wird, und wir verwenden dazu eine der Funktionen vonXPath

3.5. Abrufen von Elementen aus einem Dokument mit definiertem Namespace

Wenn in unserem XML-Dokument ein Namespace wie in der hier verwendeten Datei example_namespace.xml definiert ist, ändern sich die Regeln zum Abrufen der benötigten Daten, da unsere XML folgendermaßen beginnt:




Wenn wir nun einen Ausdruck verwenden, der "//Tutorial" ähnelt, erhalten wir kein Ergebnis. Dieser XPath-Ausdruck gibt alle<Tutorial>-Elemente zurück, die sich nicht in einem Namespace befinden. In unserer neuen example_namespace.xml werden alle<Tutorial>-Elemente im Namespace/full_archive.++

Mal sehen, wie man mit Namespaces umgeht.

Zunächst müssen wir den Namespace-Kontext festlegen, damit XPath wissen kann, wo wir nach unseren Daten suchen:

xPath.setNamespaceContext(new NamespaceContext() {
    @Override
    public Iterator getPrefixes(String arg0) {
        return null;
    }
    @Override
    public String getPrefix(String arg0) {
        return null;
    }
    @Override
    public String getNamespaceURI(String arg0) {
        if ("bdn".equals(arg0)) {
            return "/full_archive";
        }
        return null;
    }
});

In der obigen Methode definieren wir "bdn" als Namen für unseren Namespace "/full_archive" und von nun an müssen wir den verwendeten XPath-Ausdrücken "bdn" hinzufügen Elemente zu lokalisieren:

String expression = "/bdn:Tutorials/bdn:Tutorial";
nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);

Mit dem obigen Ausdruck können wir alle<Tutorial>-Elemente unter dem Namespace "bdn" abrufen.

3.6. Vermeiden von Problemen mit leeren Textknoten

Wie Sie feststellen konnten, wird im Code im Abschnitt 3.3 dieses Artikels eine neue Funktion aufgerufen, die direkt nach dem Parsen unseres XML-Dokuments in ein Dokumentobjektthis.clean(xmlDocument); ausgeführt wird

Manchmal, wenn wir Elemente, untergeordnete Knoten usw. durchlaufen und unser Dokument leere Textknoten enthält, können wir in den gewünschten Ergebnissen ein unerwartetes Verhalten feststellen.

Wir habennode.getFirstChild() aufgerufen, wenn wir alle<Tutorial>-Elemente durchlaufen, um nach den<title>-Informationen zu suchen, aber anstelle dessen, wonach wir suchen, haben wir nur "#Text" als leeren Knoten.

Um das Problem zu beheben, können wir durch unser Dokument navigieren und diese leeren Knoten wie folgt entfernen:

NodeList childs = node.getChildNodes();
for (int n = childs.getLength() - 1; n >= 0; n--) {
    Node child = childs.item(n);
    short nodeType = child.getNodeType();
    if (nodeType == Node.ELEMENT_NODE) {
        clean(child);
    }
    else if (nodeType == Node.TEXT_NODE) {
        String trimmedNodeVal = child.getNodeValue().trim();
        if (trimmedNodeVal.length() == 0){
            node.removeChild(child);
        }
        else {
            child.setNodeValue(trimmedNodeVal);
        }
    } else if (nodeType == Node.COMMENT_NODE) {
        node.removeChild(child);
    }
}

Auf diese Weise können wir jeden Knotentyp überprüfen, den wir finden, und diejenigen entfernen, die wir nicht benötigen.

4. Schlussfolgerungen

Hier haben wir gerade die standardmäßige XPath-Unterstützung eingeführt, aber es gibt viele beliebte Bibliotheken wie JDOM, Saxon, XQuery, JAXP, Jaxen oder sogar Jackson. Es gibt Bibliotheken für spezifisches HTML-Parsing wie JSoup.

XPath-Ausdrücke können von der XSLT-Sprache zum Navigieren in XML-Dokumenten verwendet werden.

Wie Sie sehen, gibt es eine Vielzahl von Möglichkeiten, wie Sie mit solchen Dateien umgehen können.

Standardmäßig gibt es eine hervorragende Standardunterstützung für das Parsen, Lesen und Verarbeiten von XML / HTML-Dokumenten. Sie finden die vollständige Arbeitsprobehere.