As XML grew in popularity, the need to access specific pieces of data directly became apparent. In July 1999, XML Path Language (XPath) was introduced in the Extensible Stylesheet Language (XSL) specification as a means to find any node within an XML document. XPath uses a non-XML syntax that closely resembles the path syntax of a file system. The language consists of location paths and expressions, as well as a few helpful functions to aid in retrieving specific data.
An XPath expression consists of two parts: a context node and a selection pattern. The context node is the context from which the selection pattern begins. Referring to books.xml from the previous section, consider this XPath expression:
book/author
If this expression were executed at the root level (its context), all <author/> nodes would be returned because the <book/> element is a child of the document element and contains an <author/> element. This expression is not very specific, so all <author/> elements are returned.
What if you want to retrieve only the <book/> element that has a specific ISBN? The XPath expression would look like this:
book[@isbn='0471777781']
The book part of the expression describes which element to retrieve. Inside of the square brackets is a condition that this element must match. The @isbn part represents the isbn attribute (@ being short for attribute). So, this expression reads "find the book elements that have an isbn attribute of '041777781'."
XPath expressions can also be very complex. Consider the following expression:
book[author[contains(text(),'McPeak')]]
This expression reads, "find the book elements that have author elements whose text contains the string 'McPeak'." Since this is a more complicated expression, it helps to break it down, working from the outside towards the inside. Removing all conditions, you have this expression:
book[...]
First, you know that a <book/> element will be returned since it is the outermost element; next come the conditions. Inside the first set of brackets, you notice the <author/> element:
author[...]
You now know you are looking for a book element with a child <author/> element. However, the children of the <author/> element need to be checked as well because the expression doesn't end there:
contains(text(),'McPeak')
The contains() function takes two arguments and returns true if the first string argument contains the second string argument. The text() function returns all the text in the given context, so the text contents of the <author/> element are passed as the first argument in contains(). The second argument passed to contains() is the search text, in this case 'McPeak'.
Important |
Note that the contains() function, like all XPath functions, is case-sensitive. |
The resulting node set is one <book/> element, because there is only one book with an author (or coauthor) whose name is McPeak.
As you can see, XPath is a useful language that makes finding specific nodes in XML data rather simple. It is no wonder Microsoft and Mozilla implemented XPath in their browsers for client-side use.
Microsoft's implementation of XPath is a part of MSXML 3.0 and later. If you are using any version of Windows XP, or have IE 6.0 or higher installed, then your computer has this capability. If not, you will need to download and install the latest MSXML package.
Microsoft chose to implement two methods that select nodes based on XPath expressions. The first, selectSingleNode(), returns the first node within its context that matches the expression. For example:
var oFirstAuthor = oXmlDom.documentElement.selectSingleNode("book/author");
This code returns the first <author/> element that is a child of a <book/> element in the context of documentElement. The result of this is the following node:
<author>Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett</author>
The second method in Microsoft's XPath implementation is selectNodes(). This method returns a NodeList, a collection of all nodes that match the pattern in the XPath expression:
var cAuthors = oXmlDom.documentElement.selectNodes("book/author");
As you may have guessed, all <author/> elements with a parent of <book/> in the context of the document element are returned. If the pattern cannot be matched in the document, a NodeList is still returned but it has a length of 0. It is a good idea to check the length of a returned NodeList before attempting to use it:
var cAuthors = oXmlDom.documentElement.selectNodes("book/author"); if (cAuthors.length > 0) { //Do something }
The X in XML stands for eXtensible. There are no predefined elements in an XML document; a developer created every element in any given XML document. This extensibility is part of what makes XML so popular, but it also inherently causes a problem: naming conflicts. For example, consider the following XML document:
<?xml version="1.0" encoding=" utf-8"?> <addresses> <address> <number>12345</number> <street>Your Street</street> <city>Your City</city> <state>Your State</state> <country>USA</country> </address> </addresses>
There is nothing out of the ordinary in this document. It simply describes an address located in the USA. But what if the following lines were added:
<?xml version="1.0" encoding=" utf-8"?> <addresses> <address> <number>12345</number> <street>Your Street</street> <city>Your City</city> <state>Your State</state> <country>USA</country> </address> <address> <ip>127.0.0.1</ip> <hostname>localhost</hostname> </address> </addresses>
This document now describes two types of addresses: a physical mailing address and a computer address. While both addresses are legitimate addresses, handling this information requires different approaches, especially since both <address/> elements contain completely different child elements. An XML processor will not distinguish the difference between these two <address/> elements, so it is up to you to do it. This is where namespaces come into play.
Namespaces consists of two parts: a namespaceURI and a prefix. The namespaceURI identifies the namespace. Generally, namespaceURIs are web site URLs, because they must be unique to access different web sites. The prefix is a local name in the XML document for the namespace. Every tag name in the namespace uses the namespace prefix. The syntax of namespace declarations are as follows:
xmlns:namespace-prefix=" namespaceURI"
The xmlns keyword tells the XML parser that a namespace declaration is taking place. The namespaceprefix is the local name used in the elements that fall under this namespace, and namespaceURI is the universal resource locator that the prefix represents.
Namespace declarations must appear before the namespace is used in the XML document. In the example, the root element contains the namespaces declarations:
<?xml version="1.0" encoding=" utf-8"?> <addresses xmlns:mail="http://www.wrox.com/mail" xmlns:comp="http://www.wrox.com/computer"> <mail:address> <mail:number>12345</mail:number> <mail:street>Your Street</mail:street> <mail:city>Your City</mail:city> <mail:state>Your State</mail:state> <mail:country>USA</mail:country> </mail:address> <comp:address> <comp:ip>127.0.0.1</comp:ip> <comp:hostname>localhost</comp:hostname> </comp:address> </addresses>
This newly edited XML document defines two namespaces: one with the prefix mail to represent a mailing address, and the other with a prefix of comp to represent a computer address. Probably the first thing you noticed was the usage of the prefixes. Every element associated with a certain address type is associated with the corresponding namespace, so every element associated as a mailing address has the mail prefix while every computer-based address has the comp prefix.
The use of namespaces avoids naming conflicts, and XML processors now understand the difference between the two address types.
Namespaces in XPath add a slight complication when using selectSingleNode() and selectNodes(). Consider the following modified version of books.xml:
<?xml version="1.0" encoding=" utf-8"?> <bookList xmlns="http://site1.com" xmlns:pub="http://site2.com"> <book isbn="0471777781"> <title>Professional Ajax</title> <author>Nicholas C. Zakas, Jeremy McPeak, Joe Fawcett</author> <pub:name>Wrox</pub:name> </book> <book isbn="0764579088"> <title>Professional JavaScript for Web Developers</title> <author>Nicholas C. Zakas</author> <pub:name>Wrox</pub:name> </book> <book isbn="0764557599"> <title>Professional C#</title> <author>Simon Robinson, et al</author> <pub:name>Wrox</pub:name> </book> <book isbn="1861006314"> <title>GDI+ Programming: Creating Custom Controls Using C#</title> <author>Eric White</author> <pub:name>Wrox</pub:name> </book> <book isbn="1861002025"> <title>Professional Visual Basic 6 Databases</title> <author>Charles Williams</author> <pub:name>Wrox</pub:name> </book> </bookList>
This newly revised document has two namespaces in use: the default namespace specified by xmlns="http://site1.com", followed by the pub namespace specified as xmlns:pub="http://site2.com". A default namespace does not have a prefix; therefore, all nonprefixed elements in the document use the default namespace. Notice that the <publisher/> elements are replaced by <pub:name/> elements.
When dealing with an XML document that contains namespaces, these namespaces must be declared in order to use XPath expressions. The MSXML DOM document exposes a method called setProperty() which is used to set second-level properties for the object. The specific property SelectionNamespaces should be set with an alias namespace for any default or external namespace. Aside from using the setProperty() method, namespace declarations are assigned just as they are in XML documents:
var sNameSpace = "xmlns:na='http://site1.com' xmlns:pub='http://site2.com'"; oXmlDom.setProperty("SelectionNamespaces", sNameSpace);
The namespaces na and pub represent the namespaces used in the XML document. Notice that the namespace prefix na is defined for the default namespace. MSXML will not recognize a default namespace when selecting nodes with XPath, so the declaration of an alias prefix is necessary. Now that the SelectionNamespace property is set, you can select nodes within the document:
var oRoot = oXmlDom.documentElement; var sXPath = "na:book/pub:name"; var cPublishers = oRoot.selectNodes(sXPath); if (cPublishers.length > 0) { alert(cPublishers.length + " <pub:name/> elements found with "+ sXPath); }
The XPath expression uses the namespaces specified in the SelectionNamespaces property and selects all <pub:name/> elements. In the case of this example, a NodeList consisting of five elements is returned, which you can then use.
The Firefox XPath implementation follows the DOM standard, which is quite different from the IE implementation. The Firefox implementation allows XPath expressions to be run against HTML and XML documents alike. At the center of this are two primary objects: XPathEvaluator and XPathResult.
The XPathEvaluator class evaluates a given XPath expression using the evaluate() method, which takes five arguments: the XPath expression string to be evaluated, the context node that the expression should be run against, a namespace resolver (which is a function that will handle the namespaces in the expression), the result type you want (ten different result types are available), and an XPathResult object to contain the results (if this argument is null, then a new XPathResult object is returned).
Before moving on, it's important to understand the various result types that can be returned from evaluate(). These are:
XPathResult.ANY_TYPE, which returns no specific type. The method returns the type that naturally results from the evaluation of the expression.
XPathResult.ANY_UNORDERED_NODE_TYPE, which returns a node set of one node that is accessed through the singleNodeValue property; null is returned if there are no matching nodes. The returned node may or may not be the first occurring node.
XPathResult.BOOLEAN_TYPE, which returns a Boolean value.
XPathResult.FIRST_ORDERED_NODE_TYPE, which returns a node set consisting of one node. This node is accessed with the singleNodeValue property of the XPathResult class. The node returned is the first occurring one in the document.
XPathResult.NUMBER_TYPE, which returns a number value.
XPathResult.ORDERED_NODE_ITERATOR_TYPE, which returns a document-ordered node set that can be iterated through using the iterateNext() method; therefore, you can easily access each individual node in the set.
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, which returns a document-ordered node set that is a snapshot of the result set. Any modifications made to the nodes in the document do not affect the retrieved results.
XPathResult.STRING_TYPE, which returns a string value.
XPathResult.UNORDERED_NODE_ITERATOR_TYPE, which returns a node set that can be iterated through; however, the results may or may not be in the same order as they appear in the document.
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, which returns an unordered snapshot node set. Any modifications made to the nodes in the document do not affect the result set.
The most common result type is XPathResult.ORDERED_NODE_ITERATOR_TYPE:
var oEvaluator = new XPathEvaluator(); var sXPath = "book/author"; var oResult = oEvaluator.evaluate(sXPath,oXmlDom.documentElement,null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); var aNodes = new Array; if (oResult != null) { var oElement; while (oElement = oResult.iterateNext()) { aNodes.push(oElement); } }
In this code, an XPathEvaluator object is created and used to evaluate the XPath expression book/author in the context of the document's root element. Because the result type is ORDERED_NOE_ITERATOR_TYPE, the evaluation returns a node set that you can iterate through using the iterateNext().
The iterateNext() method resembles the nextSibling property of a DOM node in that it selects the next node in the result set and returns null when the end of the result set is reached. This function enables you to use it in a while loop as in the previous example; as long as oElement is not null, it is added to the aNodes array through the push() method. Populating an array gives you IE-like functionality; therefore, you can use it in a for loop or access separate array elements easily.
автомобильные новости
In the syntax of the evaluate() method, you saw a reference to a namespace resolver. A namespace resolver is a function that resolves any namespace prefixes appearing in the XPath expression to a namespace URI. The namespace resolver function can be named any name you want, but it requires that a string argument be accepted (which is the prefix to check).
The resolver checks for the prefix provided by the argument and should return the namespace URI associated with it. To use the values from the IE example, you can write the following resolver:
function nsResolver(sPrefix) { switch (sPrefix) { case "na": return "http://site1.com"; break; case "pub": return "http://site2.com"; break; default: return null; break; } }
With the resolver written, you can use the following XPath expression on the modified books.xml document from the IE namespace example:
var sXPath = "na:book/pub:name"; var oEvaluator = new XPathEvaluator(); var oResult = oEvaluator.evaluate(sXPath,oXmlDom.documentElement,nsResolver, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null); var aNodes = new Array; if (oResult != null) { var oElement; while (oElement = oResult.iterateNext()) { aNodes.push(oElement); } }
This example resembles the last evaluation code. However, notice the addition to the evaluate() method: the pointer to the nsResolver() function, written earlier, is passed to the evaluate() method to handle the namespaces in the XPath expression. The remainder of the code should look familiar to you. The resulting NodeList is converted to an array by using the iterateNext() method of the XPathResult class to iterate through the result.
As you can see, the Firefox XPath implementation is quite different from the Microsoft approach; so it is helpful to use a cross-browser library that enables you to perform XPath evaluations easily.
The zXml library created by the authors provides cross-browser XPath functionality through a common interface. The object responsible for providing XPath functionality is zXPath, which has two methods.
The first method is selectSingleNode(). This method, like the IE method of the same name, returns the first node that matches a pattern. Unlike the IE implementation, this method accepts three arguments: the context node, the XPath expression string, and a string containing the namespace declarations. The namespace string should be in the following format:
"xmlns:na='http://site1.com' xmlns:pub='http://site2.com' xmlns:ns='http://site3.com'"
If you are not working with namespaces, then the first two arguments of selectSingleNode() are the only required arguments.
The returned result of selectSingleNode() is the selected XML node, or null if a match cannot be found. If the browser does not support XPath, an error is thrown stating that the browser does not have an XPath engine installed. The following example evaluates an XPath expression against the document element:
var oRoot = oXmlDom.documentElement; var oNode = zXPath.selectSingleNode(oRoot, "book/author", null); if (oNode) { alert(oNode.xml); }
This example searches for the first <author/> element contained in a <book/> element in the context of the document root. If found, the serialized form of the XML data is displayed to the user in an alert box.
The second method of zXPath is selectNodes(), which returns a node set much like the IE selectNodes() method. The syntax closely resembles that of the selectSingleNode() method above, and the arguments are exactly the same, and the same namespace rules apply. Also like selectSingleNode() an error is thrown in the event the browser does not have an XPath engine installed. The next example demonstrates the selectNodes() method:
var sNameSpace = "xmlns:na='http://site1.com' xmlns:pub='http://site2.com'"; var oRoot = oXmlDom.documentElement; var sXPath = "na:book/pub:name"; var oNodes = zXPath.selectNodes(oRoot, sXPath, sNameSpace); if (oNodes.length > 0) { alert(oNodes.length); }
This example, much like the selectSingleNode() example, searches for all author elements of a document that incorporates namespaces. If the result set has a length greater than 0, the length of the result is displayed to the user.
XPath is a powerful tool to navigate through and select certain nodes in an XML document, although it was never intended to be used as a standalone tool. Instead, it was created for use in XSL Transformations.