Pengantar XPath dengan Java

1. Ikhtisar

Pada artikel ini kita akan membahas dasar-dasar XPath dengan dukungan dalam Java JDK standar .

Kami akan menggunakan dokumen XML sederhana, memprosesnya, dan melihat bagaimana menelusuri dokumen untuk mengekstrak informasi yang kami butuhkan darinya.

XPath adalah sintaks standar yang direkomendasikan oleh W3C, ini adalah sekumpulan ekspresi untuk menavigasi dokumen XML. Anda dapat menemukan referensi XPath lengkap di sini.

2. Pengurai XPath Sederhana

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

Sekarang mari kita lihat lebih dekat elemen yang akan Anda temukan di 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);

Mari kita hancurkan:

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();

Kami akan menggunakan objek ini untuk menghasilkan pohon objek DOM dari dokumen xml kami:

DocumentBuilder builder = builderFactory.newDocumentBuilder();

Memiliki instance kelas ini, kita dapat mengurai dokumen XML dari berbagai sumber input seperti InputStream , File , URL , dan SAX :

Document xmlDocument = builder.parse(fileIS);

Sebuah Dokumen ( org.w3c.dom.Document ) mewakili seluruh dokumen XML, adalah akar dari pohon dokumen, menyediakan akses pertama kami data:

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

Dari objek XPath kita akan mengakses ekspresi dan menjalankannya di atas dokumen kita untuk mengekstrak apa yang kita butuhkan darinya:

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

Kita dapat mengkompilasi ekspresi XPath yang diteruskan sebagai string dan menentukan jenis data apa yang kita harapkan untuk menerima NODESET , NODE atau String misalnya.

3. Mari Mulai

Sekarang setelah kita melihat komponen dasar yang akan kita gunakan, mari kita mulai dengan beberapa kode menggunakan beberapa XML sederhana, untuk tujuan pengujian:

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

3.1. Ambil Daftar Elemen Dasar

Metode pertama adalah penggunaan ekspresi XPath yang sederhana untuk mengambil daftar node dari 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); 

Kita dapat mengambil daftar tutorial yang terdapat dalam simpul akar dengan menggunakan ekspresi di atas, atau dengan menggunakan ekspresi " // Tutorial " tetapi yang ini akan mengambil semua node dalam dokumen dari node saat ini tidak peduli di mana mereka berada dalam dokumen, ini berarti pada tingkat pohon apapun mulai dari node saat ini.

The NodeList itu kembali dengan menentukan nodeset ke instruksi kompilasi sebagai jenis kembali, adalah koleksi memerintahkan node yang dapat diakses dengan melewati indeks sebagai parameter.

3.2. Mengambil Node Tertentu berdasarkan ID-nya

Kita dapat mencari elemen berdasarkan id yang diberikan hanya dengan memfilter:

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

Dengan menggunakan ekspresi semacam ini, kita dapat memfilter elemen apa pun yang perlu kita cari hanya dengan menggunakan sintaks yang benar. Jenis ekspresi ini disebut predikat dan merupakan cara mudah untuk menemukan data tertentu di atas dokumen, misalnya:

/ Tutorial / Tutorial [1]

/ Tutorial / Tutorial [pertama ()]

/ Tutorial / Tutorial [posisi () <4]

Anda dapat menemukan referensi lengkap dari predikat di sini

3.3. Mengambil Node dengan Nama Tag Tertentu

Sekarang kita melangkah lebih jauh dengan memperkenalkan sumbu, mari kita lihat bagaimana ini bekerja dengan menggunakannya dalam ekspresi 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); 

Dengan ekspresi yang digunakan di atas, kami mencari setiap elemen yang memiliki keturunan dengan teks yang diteruskan sebagai parameter di variabel "nama".

Mengikuti contoh xml yang disediakan untuk artikel ini, kita dapat mencari file berisi teks “Guava” atau “XML” dan kami akan mengambil semuanya elemen dengan semua datanya.

Axes menyediakan cara yang sangat fleksibel untuk menavigasi dokumen XML dan Anda dapat menemukan dokumentasi lengkapnya di situs resmi.

3.4. Memanipulasi Data dalam Ekspresi

XPath memungkinkan kita untuk memanipulasi data juga dalam ekspresi jika diperlukan.

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

Dalam ekspresi ini kita meneruskan ke metode kita string sederhana sebagai tanggal yang terlihat seperti "ddmmyyyy" tetapi XML menyimpan data ini dengan format " dd / mm / yyyy ", jadi untuk mencocokkan hasil kita memanipulasi string untuk mengubahnya ke format data yang benar yang digunakan oleh dokumen kami dan kami melakukannya dengan menggunakan salah satu fungsi yang disediakan oleh XPath

3.5. Mengambil Elemen dari Dokumen Dengan Namespace Ditentukan

If our xml document has a namespace defined as it is in the example_namespace.xml used here, the rules to retrieve the data we need are going to change since our xml starts like this:

Now when we use an expression similar to “//Tutorial”, we are not going to get any result. That XPath expression is going to return all elements that aren't under any namespace, and in our new example_namespace.xml, all elements are defined in the namespace /full_archive.

Lets see how to handle namespaces.

First of all we need to set the namespace context so XPath will be able to know where are we looking for our data:

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 the method above, we are defining “bdn” as the name for our namespace “/full_archive“, and from now on, we need to add “bdn” to the XPath expressions used to locate elements:

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

Using the expression above we are able to retrieve all elements under “bdn” namespace.

3.6. Avoiding Empty Text Nodes Troubles

As you could notice, in the code at the 3.3 section of this article a new function is called just right after parsing our XML to a Document object, this .clean( xmlDocument );

Sometimes when we iterate through elements, childnodes and so on, if our document has empty text nodes we can find an unexpected behavior in the results we want to get.

We called node .getFirstChild() when we are iterating over all elements looking for the information, but instead of what we are looking for we just have “#Text” as an empty node.

To fix the problem we can navigate through our document and remove those empty nodes, like this:

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

By doing this we can check each type of node we find and remove those ones we don't need.

4. Conclusions

Here we just introduced the default XPath provided support, but there are many popular libraries as JDOM, Saxon, XQuery, JAXP, Jaxen or even Jackson now. There are libraries for specific HTML parsing too like JSoup.

It's not limited to java, XPath expressions can be used by XSLT language to navigate XML documents.

As you can see, there is a wide range of possibilities on how to handle these kind of files.

Ada dukungan standar yang bagus secara default untuk penguraian, pembacaan dan pemrosesan dokumen XML / HTML. Anda dapat menemukan contoh lengkap yang berfungsi di sini.