JavaによるXPathの紹介
1概要
単純なXML文書を使用し、それを処理して、その文書から必要な情報を抽出する方法を確認します。
XPathはW3Cによって推奨されている標準の構文であり、XML文書をナビゲートするための式のセットです。あなたは完全なXPathリファレンスhttps://www.w3.org/TR/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. 基本的な要素リストの取得
最初の方法は、XMLからノードのリストを取得するためのXPath式の単純な使用法です。
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);
このような式を使用することで、正しい構文を使用するだけで、検索する必要がある要素をすべて絞り込むことができます。これらの種類の式は述語と呼ばれ、ドキュメント上の特定のデータを見つけるための簡単な方法です。次に例を示します。
/チュートリアル/チュートリアル[1]
/チュートリアル/チュートリアル[first()]
/チュートリアル/チュートリアル[position()<4]
あなたは述語の完全な参照を見つけることができます
3.3. 特定のタグ名でノードを取得する
それでは、Axesを紹介しながら、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);
上記で使用されている式を使用して、子孫
<title>
を持ち、 “name”変数のパラメータとして渡されたテキストを持つすべての
<Tutorial>
要素を探しています。
この記事で提供されているサンプルのxmlに従って、テキスト“ Guava”または“ XML”を含む
<title>
を探すことができます。そして、すべてのデータを含む
<Tutorial>
要素全体を取得します。
AxesはXML文書をナビゲートするための非常に柔軟な方法を提供します、そしてあなたはそれに関する完全な文書を見つけることができますそれはhttps://www.w3.org/TR/xpath/#axes[公式サイト]です。
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
」の形式で格納します。この文書で使用されている正しいデータ形式に変換し、https://www.w3.org/TR/xpath/#corelib[XPath]によって提供される関数の1つを使用してそれを実行します。
3.5. 名前空間が定義されている文書からの要素の取得
ここで使用されているexample__namespace.xmlで定義されているように、xmlドキュメントに名前空間が定義されている場合、xmlは次のように開始されるので、必要なデータを取得するための規則は変わります。
<?xml version="1.0"?>
<Tutorials xmlns="/full__archive">
</Tutorials>
今度は“
_//Tutoria
l”のような式を使用しても、結果は得られません。その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オブジェクトに解析した直後に新しい関数
xmlDocument
が呼び出されます。
要素、子ノードなどを反復処理するときに、ドキュメントに空のテキストノードがあると、取得したい結果に予期しない動作が発生することがあります。
<title>
情報を探してすべての
<Tutorial>
要素を反復処理するときに
.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式はXMLドキュメントをナビゲートするためにXSLT言語で使用できます。
ご覧のとおり、この種のファイルを処理する方法にはさまざまな可能性があります。
XML/HTML文書の構文解析、読み取り、および処理については、デフォルトで優れた標準サポートがあります。完全なワーキングサンプルhttps://github.com/eugenp/tutorials/tree/master/xml[ここ]を見つけることができます。