1. 概要

この記事では、XPathの基本と標準のJavaJDKのサポートについて説明します。

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

XPathは、W3Cが推奨する標準の構文であり、XMLドキュメントをナビゲートするための式のセットです。 完全なXPathリファレンスはここにあります。

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

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

Document xmlDocument = builder.parse(fileIS);

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

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

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

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

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

3. はじめましょう

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

<?xml version="1.0"?>
<Tutorials>
    <Tutorial tutId="01" type="java">
        <title>Guava</title>
  <description>Introduction to Guava</description>
  <date>04/04/2016</date>
  <author>GuavaAuthor</author>
    </Tutorial>
    <Tutorial tutId="02" type="java">
        <title>XML</title>
  <description>Introduction to XPath</description>
  <date>04/05/2016</date>
  <author>XMLAuthor</author>
    </Tutorial>
</Tutorials>

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

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

コンパイル命令にリターン型として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]

述語の完全なリファレンスはここにあります。

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」変数のパラメータとして渡されたテキスト。

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

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

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は次のように開始されるため、必要なデータを取得するためのルールが変更されます。

<?xml version="1.0"?>
<Tutorials xmlns="/full_archive">

</Tutorials>

ここで、「 // Tutoria l」のような式を使用すると、結果は得られません。 そのXPath式はすべてを返します名前空間の下にない要素、および新しいexample_namespace.xmlでは、すべて要素は名前空間で定義されます / 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 」を定義しており、今後は「bdn」を追加する必要があります。 ]」要素を見つけるために使用されるXPath式に:

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

上記の式を使用すると、すべてを取得できます bdn 」名前空間。

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

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

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

電話しました 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ドキュメントの解析、読み取り、および処理には、デフォルトで優れた標準サポートがあります。 完全に機能するサンプルはここにあります。