1. Gambaran keseluruhan
Dalam artikel ini kita akan membahas asas-asas XPath dengan sokongan dalam Java JDK standard .
Kami akan menggunakan dokumen XML yang mudah, memprosesnya dan melihat cara membaca dokumen untuk mengekstrak maklumat yang kami perlukan dari dokumen tersebut.
XPath adalah sintaks standard yang disarankan oleh W3C, itu adalah sekumpulan ungkapan untuk menavigasi dokumen XML. Anda boleh mendapatkan rujukan XPath lengkap di sini.
2. Pengurai XPath Mudah
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 perhatikan lebih dekat elemen yang anda akan dapati 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 memecahkannya:
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
Kami akan menggunakan objek ini untuk menghasilkan pokok objek DOM dari dokumen xml kami:
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Mempunyai contoh kelas ini, kita dapat menguraikan dokumen XML dari banyak sumber input yang berbeza seperti InputStream , File , URL dan SAX :
Document xmlDocument = builder.parse(fileIS);
A Dokumen ( org.w3c.dom.Document ) mewakili dokumen XML keseluruhan, adalah akar pohon dokumen, menyediakan akses pertama kami untuk data:
XPath xPath = XPathFactory.newInstance().newXPath();
Dari objek XPath kita akan mengakses ungkapan dan melaksanakannya di atas dokumen kita untuk mengekstrak apa yang kita perlukan darinya:
xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
Kita boleh menyusun ungkapan XPath yang dilalui sebagai rentetan dan menentukan jenis data yang kita harapkan akan menerima NODESET , NODE atau String seperti itu.
3. Mari Bermula
Setelah kita melihat komponen asas yang akan kita gunakan, mari mulakan dengan beberapa kod 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. Dapatkan Senarai Asas Elemen
Kaedah pertama adalah penggunaan mudah ungkapan XPath untuk mendapatkan senarai nod 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 senarai tutorial yang terdapat di simpul root dengan menggunakan ungkapan di atas, atau dengan menggunakan ungkapan " // Tutorial " tetapi yang ini akan mengambil semua simpul dalam dokumen dari node semasa tidak kira di mana mereka berada di dalam dokumen, ini bermaksud pada tahap pokok apa sahaja bermula dari nod semasa.
The NodeList ia kembali dengan menentukan NODESET arahan yang menyusun sebagai jenis pulangan, adalah koleksi lebih awal nod yang boleh diakses dengan melepaskan indeks sebagai parameter.
3.2. Mengambil Node Tertentu dengan IDnya
Kita boleh mencari elemen berdasarkan id yang diberikan hanya dengan menyaring:
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 ungkapan seperti ini, kita dapat menapis elemen apa pun yang perlu kita cari hanya dengan menggunakan sintaks yang betul. Jenis ungkapan ini disebut predikat dan mereka adalah cara mudah untuk mencari data tertentu melalui dokumen, misalnya:
/ Tutorial / Tutorial [1]
/ Tutorial / Tutorial [pertama ()]
/ Tutorial / Tutorial [kedudukan () <4]
Anda boleh mendapatkan rujukan lengkap mengenai predikat di sini
3.3. Mengambil Node dengan Nama Tag Khusus
Sekarang kita melangkah lebih jauh dengan memperkenalkan sumbu, mari kita lihat bagaimana ini berfungsi 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 ungkapan yang digunakan di atas, kami mencari setiap unsur yang mempunyai keturunan dengan teks yang dilalui sebagai parameter dalam pemboleh ubah "nama".
Mengikuti contoh xml yang disediakan untuk artikel ini, kita dapat mencari mengandungi teks "Jambu" atau "XML" dan kami akan mengambil keseluruhannya elemen dengan semua datanya.
Axes menyediakan cara yang sangat fleksibel untuk menavigasi dokumen XML dan anda boleh mendapatkan dokumentasi lengkap di laman web rasmi.
3.4. Memanipulasi Data dalam Ungkapan
XPath membolehkan kita memanipulasi data juga dalam ungkapan jika diperlukan.
XPath xPath = XPathFactory.newInstance().newXPath(); String expression = "//Tutorial[number(translate(date, '/', '')) > " + date + "]"; nodeList = (NodeList) xPath.compile(expression).evaluate(xmlDocument, XPathConstants.NODESET);
Dalam ungkapan ini, kami menggunakan kaedah kami rentetan sederhana sebagai tarikh yang kelihatan seperti "ddmmyyyy" tetapi XML menyimpan data ini dengan format " dd / mm / yyyy ", jadi untuk mencocokkan hasilnya, kami memanipulasi rentetan untuk mengubahnya ke format data yang betul yang digunakan oleh dokumen kami dan kami melakukannya dengan menggunakan salah satu fungsi yang disediakan oleh XPath
3.5. Mengambil Elemen dari Dokumen Dengan Ruang Nama 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.
Terdapat sokongan standard yang hebat secara lalai untuk mengurai, membaca dan memproses dokumen XML / HTML. Anda boleh mendapatkan sampel kerja penuh di sini.