Introduction à XPath avec Java

Introduction à XPath avec Java

1. Vue d'ensemble

Dans cet article, nous allons passer en revuethe basics of XPath with the support in the standard Java JDK.

Nous allons utiliser un simple document XML, le traiter et voir comment le parcourir pour en extraire les informations dont nous avons besoin.

XPath est une syntaxe standard recommandée par le W3C. Il s'agit d'un ensemble d'expressions permettant de naviguer dans des documents XML. Vous pouvez trouver une référence XPath complètehere.

2. Un analyseur XPath simple

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;
    }
}

Regardons maintenant de plus près les éléments que vous trouverez dans lesDefaultParser:

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);

Décomposons cela:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

Nous allons utiliser cet objet pour produire une arborescence d'objets DOM à partir de notre document xml:

DocumentBuilder builder = builderFactory.newDocumentBuilder();

Ayant une instance de cette classe, nous pouvons analyser des documents XML à partir de nombreuses sources d'entrée différentes telles queInputStream,File,URL etSAX:

Document xmlDocument = builder.parse(fileIS);

UnDocument (org.w3c.dom.Document) représente l'ensemble du document XML, est la racine de l'arborescence du document, fournit notre premier accès aux données:

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

À partir de l'objet XPath, nous accéderons aux expressions et les exécuterons sur notre document pour en extraire ce dont nous avons besoin:

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

Nous pouvons compiler une expression XPath passée sous forme de chaîne et définir le type de données que nous attendons de recevoir de telsNODESET,NODE ouString par exemple.

3. Commençons

Maintenant que nous avons regardé les composants de base que nous allons utiliser, commençons par du code utilisant du XML simple, à des fins de test:



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

3.1. Récupérer une liste élémentaire d'éléments

La première méthode consiste à utiliser simplement une expression XPath pour récupérer une liste de nœuds à partir du 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);

On peut récupérer la liste du tutoriel contenue dans le nœud racine en utilisant l'expression ci-dessus, ou en utilisant l'expression «//Tutorial» mais celle-ci récupérera tous les nœuds<Tutorial> du document à partir du nœud courant peu importe où ils se trouvent dans le document, cela signifie à n'importe quel niveau de l'arborescence à partir du nœud courant.

LeNodeList qu'il renvoie en spécifiantNODESET à l'instruction de compilation comme type de retour, est une collection ordonnée de nœuds auxquels on peut accéder en passant un index comme paramètre.

3.2. Récupération d'un nœud spécifique par son ID

Nous pouvons rechercher un élément basé sur un identifiant donné simplement en filtrant:

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);

En utilisant ce type d'expressions, nous pouvons filtrer tout élément à rechercher simplement en utilisant la syntaxe correcte. Ces types d'expressions sont appelés prédicats et constituent un moyen simple de localiser des données spécifiques sur un document, par exemple:

/Tutorials/Tutorial[1]

/Tutorials/Tutorial[first()]

/Tutorials/Tutorial[position()<4]

Vous pouvez trouver une référence complète des prédicatshere

3.3. Récupération de nœuds par un nom de balise spécifique

Maintenant, nous allons plus loin en introduisant des axes, voyons comment cela fonctionne en l'utilisant dans une expression XPath:

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);

Avec l'expression utilisée ci-dessus, nous recherchons chaque élément<Tutorial> qui a un descendant<title> avec le texte passé en paramètre dans la variable «nom».

En suivant l'exemple de xml fourni pour cet article, nous pourrions rechercher un<title> contenant le texte «Guava» ou «XML» et nous récupérerons tout l'élément<Tutorial> avec toutes ses données.

Les axes fournissent un moyen très flexible de naviguer dans un document XML et vous pouvez trouver une documentation complète dans lesofficial site.

3.4. Manipulation de données dans des expressions

XPath nous permet également de manipuler des données dans les expressions si nécessaire.

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

Dans cette expression, nous transmettons à notre méthode une simple chaîne sous forme de date qui ressemble à «jjmmaaaa» mais le XML stocke ces données au format «dd/mm/yyyy», donc pour faire correspondre un résultat, nous manipulons la chaîne pour la convertir au format de données correct utilisé par notre document et nous le faisons en utilisant l'une des fonctions fournies parXPath

3.5. Récupération d'éléments d'un document avec un espace de noms défini

Si notre document xml a un espace de noms défini tel qu'il est dans le fichier example_namespace.xml utilisé ici, les règles permettant de récupérer les données dont nous avons besoin vont changer car notre fichier xml démarre de la manière suivante:




Maintenant, lorsque nous utilisons une expression similaire à «//Tutorial», nous n'obtiendrons aucun résultat. Cette expression XPath va renvoyer tous les éléments<Tutorial> qui ne sont sous aucun espace de noms, et dans notre nouvel example_namespace.xml, tous les éléments<Tutorial> sont définis dans l'espace de noms/full_archive.++

Voyons comment gérer les espaces de noms.

Tout d’abord, nous devons définir le contexte de l’espace de nommage afin que XPath puisse savoir où nous cherchons nos données:

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;
    }
});

Dans la méthode ci-dessus, nous définissons «bdn» comme nom de notre espace de noms «/full_archive», et à partir de maintenant, nous devons ajouter «bdn» aux expressions XPath utilisées pour localiser des éléments:

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

En utilisant l'expression ci-dessus, nous pouvons récupérer tous les éléments<Tutorial> sous l'espace de noms «bdn».

3.6. Éviter les problèmes de nœuds de texte vides

Comme vous pouvez le remarquer, dans le code de la section 3.3 de cet article, une nouvelle fonction est appelée juste après l'analyse de notre XML en un objet Document,this.clean(xmlDocument);

Parfois, lorsque nous parcourons des éléments, des enfants, etc., si notre document contient des nœuds de texte vides, nous pouvons trouver un comportement inattendu dans les résultats que nous voulons obtenir.

Nous avons appelénode.getFirstChild() lorsque nous itérons sur tous les éléments de<Tutorial> à la recherche des informations de<title>, mais au lieu de ce que nous recherchons, nous avons juste «#Text» comme nœud vide.

Pour résoudre le problème, nous pouvons parcourir notre document et supprimer ces nœuds vides, comme ceci:

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);
    }
}

En faisant cela, nous pouvons vérifier chaque type de nœud que nous trouvons et supprimer ceux dont nous n'avons pas besoin.

4. Conclusions

Ici, nous venons d'introduire le support fourni par défaut par XPath, mais il existe maintenant de nombreuses bibliothèques populaires telles que JDOM, Saxon, XQuery, JAXP, Jaxen ou même Jackson. Il existe des bibliothèques pour l'analyse HTML spécifique, comme JSoup.

Il n’est pas limité à java, les expressions XPath peuvent être utilisées par le langage XSLT pour parcourir les documents XML.

Comme vous pouvez le constater, les possibilités de traitement de ce type de fichiers sont très diverses.

Il existe un excellent support standard par défaut pour l'analyse, la lecture et le traitement de documents XML / HTML. Vous pouvez trouver l'échantillon de travail complethere.