 | Level: Intermediate Brett D. McLaughlin, Sr. (brett@newInstance.com), Author and Editor, O'Reilly Media, Inc.
29 Nov 2005 XPath is not traditionally considered a data binding API. It doesn't even get much attention in the XML world, except in passing as part of other specifications. But once you fully understand what XPath is and how to use it -- particularly in a Java™ programming environment -- it becomes a powerful data binding tool that's often preferable to traditional data binding APIs such as JAXB or JaxMe. Brett McLaughlin's Practical data binding column returns with the first in a two-article series that examines XPath as a data binding tool.
So far in this column, I've focused on the fairly traditional definition and use of data binding: An XML document is converted into a Java representation and used in normal Java methods (for example, getName() or setAddress()). Then, the Java object is converted back into an XML representation and usually serialized (saved) to disk. I've also looked at going the other way -- taking a Java object, converting it to XML, and then using that XML (perhaps sending it across a network connection or using it as input to another XML-consuming component in your application). These are all perfectly valid and useful data binding use-cases, but I still haven't touched on all the possibilities. In this article and the next, you'll learn about an alternative approach to data binding that uses XPath technology.
If you've worked in data binding circles for long, you're used to supplying a constraint set (such as an XML schema or DTD) and having an API generate classes for you that represent that constraint set. Then you work with those classes, loading XML data into them and then serializing their data back to XML. That's a perfectly good solution, but it's not the only way to get at the data in an XML document while still avoiding lower-level APIs such as SAX or DOM. XPath, a specification you've probably heard of, is just such an alternative. Even if you haven't heard of XPath, chances are you've used it at some point. In this article, you'll learn what XPath is, get a feel for how to use it, begin to see how it functions much like a data binding API, and even learn how to make it do some pretty cool things that are a struggle in other data binding tasks.
Always a bridesmaid...
XPath has become one of the most-often used and yet least widely known XML technologies. It's actually a crucial part of one of the most commonly used XML technologies: XML transformations. It also plays an important role in the future of the Web, because it's vital to XLink and (particularly) XPointer. And it even sneaks its way into XForms.
So how can it be that people still don't think about XPath as a standalone technology? Largely, because APIs that process XPath independently of these other languages have only recently come on the scene. However, if you've worked with XSLT or XPointer, or done more advanced XML Schema work, you're already ahead of the curve.
XPath's role in XSLT
If you've used XSLT, you've almost certainly seen XPath before and are probably at least somewhat familiar with it, even if you don't realize it. Take a look at Listing 1, an example of a very simple portion of an XSL stylesheet.
Listing 1. XPath in action in an XSL stylesheet
<xsl:template match="address">
<h1>Addresses</h1>
<hr />
<table>
<tr><th>Street</th><th>City</th><th>State</th><th>Zip Code</th></tr>
<xsl:apply-templates select="address" />
</table>
</xsl:template>
<xsl:template match="address" />
<tr>
<td><xsl:value-of select="street" /></td>
<td><xsl:value-of select="city" /></td>
<td><xsl:value-of select="state" /></td>
<td><xsl:value-of select="zipCode" /></td>
</tr>
</xsl:template>
|
When you see select="address" or select="zipCode", for example, you're seeing XPath in action. In each case, the text inside the quotation marks is an XPath expression; even though the expressions are simple, they're crucial to making the stylesheet work.
In fact, imagine trying to write even the simplest XML transformation without ever typing select in your stylesheet. You can't! That's because XPath is integral to XML transformations, and therefore to XSLT. If you consider yourself an XSL pro, you're probably a lot more competent in XPath than you thought.
XPath in XPointer
XPointer is another API -- not quite as popular as XSL, but still up-and-coming -- that makes heavy use of XPath. Listing 2 shows a basic link in one document that points to another document.
Listing 2. XPath usage within an XPointer context
<link xmlns:xlink="http://www.w3.org/2000/xlink"
xlink:type="simple"
xlink:href="cd.xml#xpointer(
/cds/cd[@title='August and Everything After'])">
|
 |
Something to think about
Notice in the code examples that XPath refers to parts of an XML document. The XPath expressions don't mention elements or attributes, but instead include direct references to names of those XML constructs. This should already have you thinking about data binding.
|
|
This is a slightly more complex piece of XPath. It selects the cd element within the root cds element, which has a title attribute equal to August and Everything After, all in the cd.xml document. I'm getting a bit ahead of myself, though; don't worry too much about syntax now. The main thing to see is that XPointer, like XSL, makes heavy use of XPath -- in fact, couldn't exist without it. Once again, XPath is an important component for selecting data.
Use of XPath in XForms
XForms is a relative newcomer to XML and isn't nearly as popular as XSL -- or even XPointer or XLink. Still, it's worth mentioning because it too makes use of XPath expressions, in the input element's ref attribute. You can set an input and bind it to a particular element (or attribute) in an XML document, as shown in Listing 3.
Listing 3. XForms components use XPath to refer to the XML document being bound to the form
<input ref="xhtml:html/body/xhtml:p[@id='greentea']/
xhtml:description" />
|
The XForms statement in Listing 3 binds the input control to a particular element in an XHTML document. Again, XPath is the key to specifying exactly what that element is. And it allows for some fairly advanced selection criteria, which I'll get into shortly.
XForms is still largely an unsupported technology, but the XPath you learn now will be a great tool if and when you decide to work with XForms. Combine this know-how with some XSL, XLinks, and XInclude, and you're in business!
Selecting content: The basics
Now that you're convinced that XPath is useful and ubiquitous, it's time to learn the syntax. If you're new to XPath, this lesson will get you started and help you get a feel for an XPath's structure. If you're an old hand at XSL or perhaps XLink and XPointer, this might help explain the why of some of those strange paths you've been constructing. You might even learn some alternate -- or better -- ways to get the same data you already were interested in.
The first thing to realize is that calling XPath a language is a bit grand. It's really a syntax for selecting and working with things in an XML document. Even the functions and operators that you can use in XPath are all about selection. You won't create variables in XPath, for example, and you won't run an XPath program. There's no such thing. If you begin to think about XPath as a clever, helpful method for selecting XML elements and attributes, and then work with the values you've selected, you'll already be ahead of most XML developers. XPath isn't about doing anything with XML as much as it is about getting things from XML.
Selecting elements
 |
Setting the context
Setting the context is not XPath's job. In fact, you can't set the context with XPath. Instead, other APIs that use XPath navigate around within a document. For example, XSLT sets the context based on the template that's applied, and XForms (as a general rule) starts you at the root context every time. So understanding your context is more about knowing the API using XPath, rather than knowing XPath itself.
|
|
The first step when it comes to XPath is to get a handle on how to reference elements. Before you can select an element, though, you need to understand the current context. The context is simply where you are in an XML document. For example, you might be at the root element -- that's your context. Or, you might be at the second address element of the first person element. Before you can move around and select anything, you need to get a grasp on your context.
Once you understand the context, XPath syntax kicks in. Consider the example XHTML document in Listing 4.
Listing 4. XHTML for the Head First Lounge
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1" />
<title>Head First Lounge Elixirs</title>
<link type="text/css" rel="stylesheet" href="../lounge.css" />
</head>
<body>
<h1>Our Elixirs</h1>
<h2>Green Tea Cooler</h2>
<p class="greentea">
<img src="../images/green.jpg" alt="Green Tea Cooler" />
Chock full of vitamins and minerals, this elixir
combines the healthful benefits of green tea with
a twist of chamomile blossoms and ginger root.
</p>
<h2>Raspberry Ice Concentration</h2>
<p class="raspberry">
<img src="../images/lightblue.jpg"
alt="Raspberry Ice Concentration" />
Combining raspberry juice with lemon grass,
citrus peel and rosehips, this icy drink
will make your mind feel clear and crisp.
</p>
<h2>Blueberry Bliss Elixir</h2>
<p class="blueberry">
<img src="../images/blue.jpg" alt="Blueberry Bliss Elixir" />
Blueberries and cherry essence mixed into a base
of elderflower herb tea will put you in a relaxed
state of bliss in no time.
</p>
<h2>Cranberry Antioxidant Blast</h2>
<p>
<img src="../images/red.jpg" alt="Cranberry Antioxidant Blast" />
Wake up to the flavors of cranberry and hibiscus
in this vitamin C rich elixir.
</p>
<p>
<a href="../lounge.html">Back to the Lounge</a>
</p>
</body>
</html>
|
If your context in Listing 4 is the html element, you can select a child element just by using its name. For example, to select the body element, you just use the XPath expression body. If you want to access the h1 element nested within body, use body/h1. If you think that this resembles a directory path, you're thinking along the right lines. Selecting an element involves using the form element name/child element name.
However, before you get too far you should realize that an XPath expression can return more than one element. The resulting set of elements is called a node set. (In fact, all entities that you can select with XPath -- elements, attributes, and values -- are called nodes.) Take, for example, this path:
Five different p elements are children of the body element in Listing 4, so that expression returns a set of five nodes (not just the first p element, as you might expect). Be careful; sometimes you get exactly what you ask for, which might very well be more than you meant to ask for.
There's more to be careful about. I haven't yet shown you an expression that does anything but return an actual element. And you've yet to get the values of those nodes. If you want the value of an element (assuming it holds textual data), you need to use the text() function. To get the text of that first h1, you use body/h1/text(); in the case of Listing 4, that returns Our Elixirs.
Selecting attributes
Of course, you can get a lot more than just elements. You've already seen hints that it's possible to select attributes as well. To select an attribute, use the @ sign before the attribute name; otherwise, the notation is pretty similar.
Returning to the example in Listing 4, you could use an XPath like head/meta/@http-equiv to return the value of the meta element's http-equiv attribute. Similarly, head/link/@type returns the link element's type attribute.
Also realize that when you select an attribute, you get its value -- not just a node, as you saw with elements. So the return value (sort of a misnomer, but still a useful way to think about things) for an attribute selector is a value; for an element, it's a node set (containing a single node).
Moving around the document
I've shown you fairly ideal situations so far; specifically, it certainly helps to have the context you're working with as the root of a document. But that's obviously not always the case. Here's where that directory-structure metaphor really helps. To move up a level from the current context, just use the .. operator. So, suppose that the context is the body element, and you want the title of the page (contained in the title element under the document's head), you could use ../head/title/text(). If this is starting to feel like you're changing directories on UNIX or a Mac OS X terminal -- good!
What if your context is the img element in the second p, underneath the body, and you want that same title? You can use ../../../head/title/text(). But counting all of those ../ gets to be tedious quickly. Here's where jumping straight to the root element helps; if you're thinking UNIX again, it should be no surprise that you use an initial / to do that. That same selector in a much more convenient format is /html/head/title/text(). It's no shorter, but it's certainly much clearer. With ../ and / at your disposal, you're ready to get anywhere you need to.
Multiple selections
When you realize that you can select multiple nodes with a single XPath expression, things can get interesting. You can do this with a simple expression that just happens to correspond to multiple nodes, such as /html/body/h2. But you can also refine those selections further through the use of wildcards. Three wildcards are available in XPath:
* matches any element, regardless of the element's name
node() matches all node types (elements, text nodes, comments, attributes, and so on)
@* matches all attribute nodes, regardless of name
For example, you might select all the direct child elements of the body element with /html/body/*. You might select all the attributes of the meta tag with /html/head/meta/@*.
In all of these cases, remember that you're grabbing a set of nodes, so you won't end up with a particular value (unless you've grabbed an attribute, which I'll cover shortly). However, as long as you have a method, function, or template to handle multiple nodes, these are important additions to your toolkit.
Getting fancy
The basics are nice, but sometimes you need a little something extra -- something to dazzle your cube-mate, say, or maybe you need just some particular bit of extra functionality. In such cases, the basics might not be enough. Though hardly an exhaustive look at XPath, here are some of those little advanced tricks that might help you get just the node (or nodes) you need in your XPath applications.
Getting more general
So far, all the paths you've seen have been about selecting a node, assuming you know exactly where that node is. For example, you know where the title or img or p you want is in your document's structure and just need to navigate to it. But at times you might want to break out of the structure a bit and just grab certain elements, regardless of position (or regardless of position given a starting context). You can do this by using descendant selectors.
A descendant selector is represented by a double-slash: //. You use it to tell XPath to select all the specified nodes regardless of how deeply they are nested. Take, for example, this simple XHTML-specific XPath:
This XPath selects all the table elements nested within the XHTML's body element -- whether they are nested directly within the body (like /html/body/table) or several levels deep (like /html/body/table/tr/td/table). In this case, the nested table is selected as well as the top-level table).
 |
Not quite XML
In standard XML terminology, attributes do not belong to elements; instead, they are associated with an element and sometimes are said to be on the element. However, XPath has no capability for dealing with a relationship like that between an element and its attributes. So, it does the best it can: It treats the attributes as if they belong to the element they are on. So to get the class attribute from a p, you use p/@class. To get the element that an attribute is on, you might use @class/... You're actually moving up one level from the attribute to select the element. It's not technically perfect XML, but it's absolutely correct XPath.
|
|
This really gets cool when you start to combine it with attributes (using the @). For example, say you want to select all elements that have id attributes. You could use //@id -- this jumps back to the root element and then selects all id attributes in the document. However, you actually want all the elements, not the actual attributes, so you need to move up one level from the attribute, to the element that contains it:
You should start to see how combining these various approaches can give you some cool results.
Matching criteria
Suppose that you have a particular criterion that you want to base a match on; for example, you want the p element that has a class attribute with the value greentea. That turns out to be pretty easy in XPath, once you learn how to use brackets. Here's an example:
/html/body/p[class="greentea"]
|
The brackets let you indicate a condition; you can use = or even < and >. You might want to make that a broader expression:
Starting to get some ideas here? You can get even wilder; how about selecting all elements in the greentea class, regardless of element type:
//@class[.="greentea"]/..
|
This one probably looks a little odd to you, but it's easy to walk through. First, the // means to start at the root and select all elements (that match the selectors and criteria that follow), regardless of where they are nested. Next, the @class selects all class attributes in the document. The [.="greentea"] that follows is a little weird. The ="greentea" part is easy; it matches the greentea value to whatever is on the left of that equation. In this case, that's ., which you haven't seen before. Think about directories again, though; .. selects the parent node (or directory, in that analogy), and . selects the current node. So //@class[.="greentea"] selects all class attributes that have a value of greentea. Then, you just need to move from each of those to the elements that they are on:
//@class[.="greentea"]/..
|
This might look weird right now, but get used to all of these strange expressions. They can really help out when you want a particular element, attribute, or node set.
Calculations
As you start to use attribute selectors more and more, you'll end up working with values (from attributes) as much as you work with nodes. And if you work with fairly typical XML, you'll run into numbers. XML documents often assign numerical values to attributes (and sometimes elements). So this section focuses on the result of expressions like /people/person[firstname = "John"]/@born and /people/person/numChildren/text() (yes, I realize that it's atypical for someone to use an element for the number of children, but go with the example here).
In these cases, you might find XPath's calculation capability helpful. Just use +, -, and *, as you would in any other programming language. Add to that div for division, and mod for modulo (taking the remainder of the division of two numbers). For example, if you have an XML document that has people's birth years in four-digit form, and you want that number in two-digit form, you first get the actual birth year with something like this:
/people/person/birthdate/@year
|
That gets you the birth year (perhaps 1976 or 1945). Then, you could just subtract 1900:
(/people/person/birthdate/@year) - 1900
|
Of course, that's pretty limited; new babies born in this millennium and historical figures would both cause this formula to break down. So, go with mod:
(/people/person/birthdate/@year) mod 100
|
(By the way, don't tell the Y2K folks we're ignoring the first two digits of a four-digit year.)
String tricks
Last but not least, XPath offers some nice string-handling capabilities. XML is ultimately a whole lot of text, and the values of attributes and data within elements are usually text, so don't be surprised that XPath supports some string manipulation. Here are just a few of the functions that XPath offers for working with strings:
string() converts data into a string format, if it's not already in one.
starts-with(full-string, start-string) returns a boolean value; it checks to see if full-string starts with start-string.
contains(full-string, contains-string) returns a boolean value; it checks to see if full-string contains contains-string.
string-length(string) returns the length of string.
normalize-space(string) trims surrounding and internal whitespace from string.
Most of these are pretty self-explanatory. starts-with("McLaughlin", "Mc") returns true, as does contains("McLaughlin", "augh"). string-length("Brett") obviously returns 5, and normalize-space(" Brett McLaughlin ") returns "Brett McLaughlin". Easy enough, right? Of course, you can (and should) apply all of these to return values from XPath expressions, like /html/body/p[class='greentea']. So to get just the text from that p in Listing 4, use normalize-space() like this:
normalize-space(/html/body/p[class='greentea']) |
What's really cool is that you can use string() to pull just the text from multiple elements. For example, if you want to take all the text from all the p elements in the XHTML shown in Listing 4, you could do this:
normalize-space(string(//p)) |
Walk through this last example, and it will make lots of sense:
//p selects all the p elements in the document, regardless of position.
string(//p) takes all the content from those elements and stuffs it into one big string. However, that string includes tons of extra useless whitespace, so you've still more work to do.
normalize-space(string(//p)) takes that content, normalizes the whitespace, and gives you a nice set of text, just as you want.
Wasn't this a data binding article?
By this point, you might have forgotten that you're reading an article in a series about data binding. But resist the temptation to lump this in with all the other X* articles and APIs you've run across in the last several years. You really have just read about another approach to data binding. Consider that the point of data binding is to preserve the logical (or semantic, if you prefer a ten-cent word) meanings assigned to an XML document in Java code. If an XML document has an address element with children named street, city, and state, then you want to get the value of these elements with something like getAddress().getStreet() or perhaps:
Address address = getAddress();
System.out.println(address.getCity() + ", " + address.getState());
|
That's in some ways the very essence of data binding. But realize that with XPath, you can do these very same things! You can get the street with an XPath expression like address/street/text() or maybe /person[last-name="Gosling"]/address[@type="work"]/city. This doesn't immediately look the same as the preceding example, but it still uses the XML data logically; you're asking for person and address and street rather than the first child or the second text node or the attribute. That's incredibly important. XPath deals with XML logically, rather than structurally. That is ultimately what data binding is about -- working with data in a logical manner, rather than worrying about its structure.
Lest you be misled, you do still need to know a bit about structure to use XPath. For example, the @ operator only works on attributes, so you must know whether type -- part of an address -- is represented as a child element or an attribute. In traditional data binding, you'd just call getAddress().getType() and not need to deal with that level of structure. However, that's a fairly small price to pay for not dealing with lots of generated classes, extra classpath considerations, waiting for a marshalling or unmarshalling process, and all the other downsides of traditional data binding.
All that's left is to add in the Java language part of this equation: the ability to take an XML document and an XPath expression, and get the result of that expression in a Java-friendly way. I'll tackle that in Part 2, and you'll soon be using XPath alongside your other XML-centric tools in Java programming. You may even find that in many cases, XPath is a better tool for data binding than working with generated classes and an API such as JAXB.
Resources - Participate in the discussion forum.
- XML Path Language (XPath)
Version 1.0: Read the W3C Recommendation.
- XML Path Language (XPath) 2.0:
Find out more about this superset of XPath 1.0, which has added capability to support a richer set of data types, and to take advantage of the type information that becomes available when documents are validated using XML Schema.
- "XML for Data: What's new in XPath 2.0?" (developerWorks, September 2002): Get a fix on the differences between XPath 1.x and 2.0.
- "Get started with XPath" (developerWorks, May 2004): Not familiar with XPath? Need a refresher? Take this introductory tutorial by Bertrand Portier.
- The Extensible Stylesheet Language Family (XSL): XPath is a critical part of XSL and XSLT. Learn more about the XSL family of specifications at the W3C, including XSL Transformations (XSLT) Version 1.0.
- XQuery 1.0 and XPath 2.0 Data Model (XDM): Along with XPath 2.0, XQuery attempts to define a data model that will work with both specifications.
- "Toward an XPath API": Understand what led to the original 1.0 XPath API with this article by Leigh Dodds.
- Java and XML: The upcoming third edition of Brett McLaughlin's book (O'Reilly Media, Inc.) will devote an entire chapter to XPath.
- XML in a Nutshell: Pick up this great all-in-one XML resource by Elliotte Rusty Harold and W. Scott Means (O'Reilly Media, Inc.), which includes a chapter devoted to XPath.
-
developerWorks XML zone: Find more XML resources here, including articles, tutorials, tips, and standards.
-
developerWorks Java technology zone: Access hundreds of articles, tutorials, and tips to help you make the most of the Java-language technology and related applications.
-
IBM Certified Solution Developer -- XML and related technologies: Find out how to get certified.
About the author  | 
|  | Brett McLaughlin has worked in computers since the Logo days. (Remember the little triangle?) In recent years, he's become one of the most well-known authors and programmers in the Java technology and XML communities. He's worked for Nextel Communications, implementing complex enterprise systems; at Lutris Technologies, actually writing application servers; and most recently at O'Reilly Media, Inc., where he continues to write and edit books that matter. His most recent book, Java 5.0 Tiger: A Developer's Notebook, is the first book available on the newest version of Java technology, and his classic Java and XML remains one of the definitive works on using XML technologies in the Java language. |
Rate this page
|  |