JavaによるXPathの紹介

Javaを使用したXPathの紹介

1. 概要

この記事では、the basics of XPath with the support in the standard Java JDKについて説明します。

単純なXMLドキュメントを使用して処理し、ドキュメントから必要な情報を抽出する方法を確認します。

XPathはW3Cが推奨する標準構文であり、XMLドキュメントをナビゲートするための一連の式です。 完全なXPathリファレンスhereを見つけることができます。

2. シンプルなXPathパーサー

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

ここで、DefaultParserにある要素を詳しく見てみましょう。

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

それを分解しましょう:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

このオブジェクトを使用して、xmlドキュメントからDOMオブジェクトツリーを生成します。

DocumentBuilder builder = builderFactory.newDocumentBuilder();

このクラスのインスタンスがあると、InputStreamFileURLSAXなどのさまざまな入力ソースからのXMLドキュメントを解析できます。

Document xmlDocument = builder.parse(fileIS);

Documentorg.w3c.dom.Document)は、XMLドキュメント全体を表し、ドキュメントツリーのルートであり、データへの最初のアクセスを提供します。

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

XPathオブジェクトから式にアクセスし、ドキュメントに対してそれらを実行して、必要なものを抽出します。

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

文字列として渡されたXPath式をコンパイルし、たとえば、NODESETNODE、またはStringなどの受信を期待するデータの種類を定義できます。

3. はじめましょう

使用する基本コンポーネントに目を向けたので、テスト目的でいくつかの単純なXMLを使用したコードから始めましょう。



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

3.1. 要素の基本リストを取得する

最初の方法は、XPath式を使用して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);

上記の式を使用するか、式「//Tutorial」を使用して、ルートノードに含まれるチュートリアルリストを取得できますが、これにより、ドキュメント内のすべての<Tutorial>ノードが現在のノードから取得されます。それらがドキュメント内のどこにあるかは、現在のノードから始まるツリーのどのレベルでも意味します。

戻り値の型としてコンパイル命令にNODESETを指定することによって返されるNodeListは、パラメーターとしてインデックスを渡すことによってアクセスできるノードの順序付けられたコレクションです。

3.2. IDによる特定のノードの取得

フィルタリングするだけで、特定のIDに基づいて要素を検索できます。

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

この種の式を使用することにより、正しい構文を使用するだけで、検索する必要のある要素をフィルタリングできます。 これらの種類の式は述語と呼ばれ、ドキュメント上の特定のデータを見つける簡単な方法です。次に例を示します。

/Tutorials/Tutorial[1]

/Tutorials/Tutorial[first()]

/Tutorials/Tutorial[position()<4]

述語hereの完全なリファレンスを見つけることができます

3.3. 特定のタグ名によるノードの取得

次に、軸を導入して、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);

上で使用した式を使用して、「name」変数のパラメーターとして渡されたテキストを持つ子孫<title>を持つすべての<Tutorial>要素を探しています。

この記事で提供されているサンプルxmlに従って、「Guava」または「XML」というテキストを含む<title>を検索し、<Tutorial>要素全体とそのすべてのデータを取得します。

軸は、XMLドキュメントをナビゲートするための非常に柔軟な方法を提供し、official siteで完全なドキュメントを見つけることができます。

3.4. 式のデータの操作

XPathを使用すると、必要に応じて式内のデータも操作できます。

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

この式では、「ddmmyyyy」のような日付として単純な文字列をメソッドに渡しますが、XMLはこのデータを「dd/mm/yyyy」の形式で格納するため、結果と一致するように文字列を操作して変換しますドキュメントで使用されている正しいデータ形式に変換し、XPathで提供される関数の1つを使用して実行します

3.5. 名前空間が定義されたドキュメントからの要素の取得

ここで使用されているexample_namespace.xmlのようにxmlドキュメントに名前空間が定義されている場合、必要なデータを取得するルールは、xmlが次のように開始されるため変更されます。




ここで、「//Tutorial」のような式を使用すると、結果は得られません。 そのXPath式は、どの名前空間にも属さないすべての<Tutorial>要素を返します。新しいexample_namespace.xmlでは、すべての<Tutorial>要素が名前空間/full_archive++

名前空間の処理方法を見てみましょう。

まず、ネームスペースコンテキストを設定して、XPathがデータをどこで探しているかを知ることができるようにする必要があります。

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

上記の方法では、名前空間「/full_archive」の名前として「bdn」を定義しています。今後は、使用するXPath式に「bdn」を追加する必要があります。要素を見つけるには:

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

上記の式を使用すると、「bdn」名前空間の下にあるすべての<Tutorial>要素を取得できます。

3.6. 空のテキストノードのトラブルの回避

お気づきかもしれませんが、この記事の3.3セクションのコードでは、XMLをDocumentオブジェクトthis.clean(xmlDocument);に解析した直後に新しい関数が呼び出されます。

要素、子ノードなどを反復処理するときに、ドキュメントに空のテキストノードがある場合、取得する結果に予期しない動作が見つかることがあります。

<title>情報を探してすべての<Tutorial>要素を反復処理するときに、node.getFirstChild()を呼び出しましたが、探しているものの代わりに、空のノードとして「#Text」があります。

この問題を解決するには、次のようにドキュメント内を移動して空のノードを削除します。

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

これを行うことで、見つけた各タイプのノードをチェックし、不要なノードを削除できます。

4. 結論

ここでは、デフォルトのXPathが提供するサポートを紹介しましたが、現在はJDOM、Saxon、XQuery、JAXP、Jaxen、またはJacksonといった多くの人気のあるライブラリがあります。 JSoupのような特定のHTML解析用のライブラリもあります。

Javaに限定されず、XPath式をXSLT言語で使用してXMLドキュメントをナビゲートできます。

ご覧のとおり、これらの種類のファイルの処理方法にはさまざまな可能性があります。

XML / HTMLドキュメントの解析、読み取り、および処理には、デフォルトで優れた標準サポートがあります。 完全に機能するサンプルhereを見つけることができます。