[Update: see below]. A few years ago, Eric van der Vlist put together a proof of concept XML schema language called Examplotron. The clever part of Examplotron is that the schema for a given XML document is that document itself; a document is its own schema. This allows schemas to be designed by writing down example documents (examplotron, get it?) which can then be generalised automatically to produce a RELAX NG schema for those documents and other documents like them. Clever. Now, what if XPath worked like that?
XPath can be used to write patterns or selectors that match or select parts of an XML document for further processing. However, XPath itself is not XML, it is a textual query language that combines the syntax of UNIX file paths with SQL-like predicates. Taking inspiration from Examplotron, we can try defining patterns to match fragments of XML by writing literal fragments of XML. For example, the pattern equivalent to the XPath foo/bar/baz would be:
<foo>
<bar>
<baz/>
</bar>
</foo>
This pattern seems fairly obvious, although it is a bit more verbose than the original XPath. How about a pattern to match a <h1> element followed by a <p> element? We might use this to find the first paragraph of an XHTML document, perhaps to apply some special formatting to it.
<h1/> <p/>
In this case, the pattern looks a little clearer than the equivalent XPath, h1/following-sibling::p. On a brief tangent, CSS3 actually has a direct sibling selector to express this: h1 + p. We have implemented this selector in Prince and it is quite handy for situations like this.
While the above pattern is easy to understand, it’s not clear what we can do with it once it matches some elements. The XPath and the CSS selector match only one element, the <p>, which can then be transformed by an XSLT template or styled with CSS rules. The pattern on the other hand matches a fragment of XML consisting of two consecutive elements, just like the regular expression ab matches two consecutive characters. As with regular expressions, the obvious thing to do once you’ve found something that matches the pattern is to replace it with something else:
<ex:search> <h1/> <p/> </ex:search> <ex:replace> <h1/> <p class="first"/> </ex:replace>
Now we have two patterns: we search the document for an instance of the first pattern, and replace it with an instance of the second pattern. This is exactly like the regular expression substitution s/ab/ac/ which will replace ab with ac, except that instead of matching characters we’re matching XML elements.
There are some details that I’ve skimmed over in this example, such as what if the elements have existing content or attributes, do we copy them across implicitly? What if we want to match an element that doesn’t have a particular attribute? Some patterns cannot be written explicitly, and we would need to introduce some helper elements and attributes in a separate namespace to express what we want. For example, we might want to throw away paragraph elements that have no content:
<ex:search>
<p>
<ex:empty/>
</p>
</ex:search>
<ex:replace>
<!-- nothing -->
</ex:replace>
To finish off, here is an example of a pattern for reversing author names in a citation, so that “Smith John” becomes “John Smith”:
<ex:search> <lastName/> <firstName/> </ex:search> <ex:replace> <firstName/> <lastName/> </ex:replace>
When I tried writing this in XSLT, this is the first solution that occurred to me:
<xsl:template match="lastName[following-sibling::firstName]"> <xsl:copy-of select="following-sibling::firstName"/> <xsl:copy-of select="."/> </xsl:template> <xsl:template match="firstName[preceding-sibling::lastName]"> <!-- nothing --> </xsl:template>
Can anyone think of a simpler way to express this transformation in XSLT?
Update
Boy, did I get that wrong. As Andrew Houghton points out below, the XPath that I gave for selecting a <p> element immediately following an <h1> element is insufficiently specific, as it will match all of the <p> elements that occur after the <h1>, not just the first. This is equivalent to the CSS indirect sibling selector, h1 ~ p, not the direct sibling selector, h1 + p.
The obvious but incorrect solution is to add a position predicate to restrict the XPath to select only the first paragraph, like this: h1/following-sibling::p[1]. However, this will select a <p> element that does not directly follow the <h1>, for example the paragraph in this document, which follows a table:
<h1>Heading</h1> <table>...</table> <p>This paragraph does not immediately follow the heading.</p>
To select only paragraphs that immediately follow headings we need to select the first element that follows the heading and then check that it is a paragraph, like this: h1/following-sibling::*[1]/self::p. Great! It is a lot more verbose than the CSS h1 + p selector, but at least it should work now.
Except that in XSLT, it doesn’t work. You see, I forgot that XSLT pattern syntax is a restricted subset of XPath, and can only use the child and attribute axes. This means that you cannot write an XSLT template that matches this XPath:
<xsl:template match="h1/following-sibling::*[1]/self::p">
Instead you need to write it the other way around and place the sibling axis inside a predicate:
<xsl:template match="p[preceding-sibling::*[1]/self::h1]">
Finally, the XSLT templates that I wrote to swap the order of lastName and firstName elements also need similar adjustments to their XPaths:
<xsl:template match="lastName[following-sibling::*[1]/self::firstName]"> <xsl:copy-of select="following-sibling::firstName[1]"/> <xsl:copy-of select="."/> </xsl:template> <xsl:template match="firstName[preceding-sibling::*[1]/self::lastName]"> <!-- nothing --> </xsl:template>
As always, mistakes are the portals of discovery. So, the challenge stands: can anyone write an XSLT transform to swap the order of two elements that is simpler, cleaner, and more obvious than this monstrosity?


Two points on your article. The first is on your h1 p example. I believe that you have to be careful with the XPath expression h1/following-sibling::p. This XPath expression will return multiple p nodes which you might not have expected. For example if you had the following siblings: h1 p table p, the XPath expression would return both p nodes. You probably wanted to constrain that XPath expression to h1/following-sibling::*[1]/self::p, to insure that you only match an h1 node and its immediately following sibling that is a p node.
The second point follows along the same lines with your XSLT solution:
match="lastName[following-sibling::*[1]/self::firstName]"
select="following-sibling::*[1]/self::firstName"
match="firstName[preceding-sibling::*[1]/self::lastName]"
FYI, in case you might be thinking you could replace h1/following-sibling::*[1]/self::p with h1/following-sibling::p[1] consider the following sibling node set: h1 table p
h1/following-sibling::*[1]/self:p returns an empty node set
h1/following-sibling::p[1] return p
Here is my take on representing Xpath expressions in XML.
If the goal is to improve readability then I belive that a better approach would be to provide a graphical representation of the expression, akin to those used for XML schema in various editors. Of course, translating xpath notation into XML would provide flexibility in this area.
The downside of using an XML version of an xpath as documentation is verbosity (because a "complete" XML schema for xpath would be fairly complex).
Footnote: As an example, the Axis diagrams in Michael Kays book are IMHO worth more than a thousand words.
<xsl:template match="element()[lastName][firstName]">
<xsl:element name="{name(.)}">
<xsl:copy-of select="firstName" />
<xsl:copy-of select="lastName" />
</xsl:element>
</xsl:template>
CKnell, that's a good solution, although it might be better to use xsl:copy to create the element, in case it is in a namespace:
<xsl:template match="element()[lastName][firstName]">
<xsl:copy>
<xsl:copy-of select="firstName"/>
<xsl:copy-of select="lastName"/>
</xsl:copy>
</xsl:template>
However, your solution assumes that firstName and lastName are grouped together by a parent element, which makes the problem a lot easier. What about matching two consecutive elements that are not grouped together?
Using the name() function, namespace prefixes are copied (unlike local-name()), so I don't understand the objection.
The solution I offered matched the given XML source document, so I don't understand the next remark either. Since XML documents have exactly one root element, all other elements will be descendants of it. In the proferred source document, firstName and lastName are children of ex:search. Are you proposing a source document where firstName and lastName will be children of different parents? Will these different parent elements be siblings or will they be nested somehow?
"Grouped together" is not XML-speak, so you need to clarify your meaning.
I'd need to see some concrete example to think over.
I'd like to ammend my last comment. Please change, "In the proferred source document, firstName and lastName are children of ex:search." to, "In the proferred source document, firstName and lastName are children of ex:search and ex:replace."
Sorry, I wasn't clear. The search/replace XML is an example of a pattern that could be applied to another XML document, one like this perhaps:
<citation>
<lastName>Smith</lastName>
<firstName>John</firstName>
<title>To Catch a Walrus</title>
<date>1967</date>
...
Or the <h1/><p/> example, which also has two sequential elements that have other preceding and following siblings. By not being grouped together I meant that they are not enclosed in a parent element with no other siblings, like this: <div><h1/><p/></div>. Grouping them like this would make them much easier to select with XPath, as you could just select the parent element. However, you can't rely on this, and I'm curious as to how XSLT and XPath can handle this situation, as it seems that a pattern matching mechanism similar to regular expressions makes it easier.