XSLT and XQuery Compared

We've looked at the factors that caused XSLT and XQuery to differ . In this section we examine the differences between the two languages in detail. Wherever possible, I try to explain why the differences exist, though in many cases, the only real explanation is that two different design teams inevitably come up with different answers to the same problem.

As we have seen, XSLT and XQuery share XPath as a common core . In contrasting the languages, therefore, we mainly need to look at the parts outside XPath. However, although there is a single XPath definition, this definition allows some flexibility to the host language, so in practice there are some differences even at the XPath level.

It's worth pointing out that the similarities between the two languages are considerable:

  • Both languages have facilities for constructing new nodes in an XML result tree. In fact, both languages have two flavors of this: a direct XML-like syntax in which the elements to be constructed are written directly in their XML form, and an indirect syntax in which the names of elements or attributes need to be computed at run-time.

  • Both languages allow the definition of user -written functions, which can be called using the XPath function call mechanism. In the case of XSLT, this is a new facility in XSLT 2.0. (XSLT 1.0 allowed functions to be simulated using named templates, but these could not be called directly from within XPath expressions.)

  • Both languages provide control structures for nested iterations or joins: the FLWOR expression in the case of XQuery, and nested xslt:for-each instructions in the case of XSLT. (In XSLT 2.0, this is supplemented by a for expression in XPath 2.0, which is a subset of the XQuery FLWOR expression.)

  • Both languages allow variables to be defined. In both cases the variables are read-only. Both are declarative functional languages with no assignment statements.

XML-Based Syntax

The syntax for XSLT is based on XML. A stylesheet is an XML document. As we saw earlier, there are two main reasons for this: It makes it easy to write stylesheets that contain large chunks of HTML to be copied directly into the result document, and it makes it possible to use XSLT to transform stylesheets.

XQuery chose not to take this route (there is an XML representation of queries called XQueryX, but it is not really suitable for human consumption, as it represents a very low-level view of the XQuery parse tree). Instead, XQuery has a syntax that mimics XML where necessary, particularly when constructing target elements. Sometimes the syntax is very close to XSLT; for example, the following snippet could be written in either language:

 <a href="{@href}">see below</a> 

The difference is that in XSLT, this really is XML (it is parsed by an XML parser, and must follow all the XML rules), whereas in XQuery it merely mimics XML. This makes it possible to nest expressions in XQuery in a way that is not possible in XSLT; for example, it is possible (though not very useful) in XQuery to write

 <item size="{ count((<b/>, <c/>, <d/>)) }"/> 

which would be quite impossible in XSLT because it is not legal XML (attributes cannot contain elements). XQuery thus achieves better composability than XSLT, but at a price: Because it is not pure XML, there are new rules to learn regarding how whitespace and character references are handled, and standard XML tools (such as editors) cannot be used to manipulate the query text. There's a good reason for this choice: XQuery is likely to be often used as an embedded language with programming languages such as Java and C#, or even within SQL, where XML tools would not be much use anyway.

Semantics of Element Construction

Behind the superficially similar facilities for constructing elements and attributes in a result tree, there has until recently been a significant difference in the semantics of the processing model. In the latest working drafts, however, XSLT 2.0 has moved closer to the XQuery model.

XSLT 1.0 insists that an entire tree be constructed before any part of it can be accessed using XPath expressions. In XSLT 1.0, it was not even possible to access a constructed tree ”known as a result tree fragment ”without using the node-set() extension function, which, while widely implemented, is not actually part of the standard. This restriction has disappeared in XSLT 2.0, but it remained true until the May 2003 draft that XPath operations could only operate on trees representing a complete document (that is, a tree with a document node at its root). This is a natural consequence of the two-language model, whereby XSLT instructions can call XPath expressions but not vice versa. A temporary tree is always constructed by means of a variable declaration, and the nodes within the tree can only be accessed by an XPath expression that references this variable: The scope rules on variables ensure that the tree can always be fully constructed before any of its nodes are referenced. This is illustrated in Listing 3.2.

Listing 3.2 Constructing a Temporary Tree in XSLT
 <xslt:variable name="tree">   <calendar>     <month number="1" length="31"/>     <month number="2" length="{if ($leap-year) then 29 else 28}"/>     <month number="3" length="31"/>     <month number="4" length="30"/>     . . .     <month number="12" length="31"/>   </calendar> </xslt:variable> . . . <xslt:value-of select="sum($tree/calendar/month/@length)"/> . . . 

The fact that trees were not accessible while they were incomplete means that it was possible to describe tree construction in XSLT using a top-down model, in which parent nodes were constructed conceptually before their children. Of course, in a functional language the actual order of execution is not defined, so this was merely a way of describing the effect of the language and not necessarily a description of how an actual implementation works. In practice, though, most XSLT 1.0 processors probably followed this model fairly closely.

In XQuery, by contrast, an element constructor is just an expression and can be used in any place where other kinds of expressions may appear. For example, it is quite legitimate to write the following:

 sum( <calendar>   <month number="1" length="31"/>   <month number="2" length="if ($leap-year) then 29 else 28"/>   <month number="3" length="31"/>   <month number="4" length="30"/>   . . .   <month number="12" length="31"/> </calendar> / month / @length ) 

In this example, although the difference looks fairly superficial, it has considerable implications for the detailed semantics of the model. XQuery treats an expression that constructs an element or attribute node in the same way as any other expression. This means that the semantics are described in a bottom-up way. Just as in the expression x * (y “ 1) the subtraction is evaluated first, and then the multiplication, so in the expression <a>{$x+1}</a> , we first do the addition, then we create a text node containing the result, then we construct an element node as the parent of this text node.

This means that in XQuery it is possible to manipulate partially constructed trees (trees that do not have a document node as their root). For example, a path expression may reference an attribute node that is not connected to any element. The attribute can then be attached to an element "later," as shown below:

 let $att1 := attribute code { "23" },     $att2 := attribute desc { "24" } return if ($condition)   then <a> {$att1} </a>   else <a> {$att2} </a> 

This also means, of course, that the attribute node represented by $att1 or $att2 can be added to several different elements. Because an attribute cannot actually have two parents, the semantics require an identical copy to be made each time the attribute is attached to an element. In the formal model, this copying happens all the way up the tree: Each time a child node is added to a parent node, the child node is copied. In practice, XQuery processors will usually be able to avoid this copying, and in the vast majority of cases will use the same top-down implementation that an XSLT 1.0 processor uses; but the formal model is completely different, to allow for the increased generality of the language.

Because XQuery copies subtrees implicitly when they are attached to a new parent, it does not require an explicit instruction to do the copying. By contrast, XSLT 1.0 built an entire tree first, and then allowed it to be copied explicitly using the xslt:copy-of instruction if required. In the most recent XSLT 2.0 drafts, the processing model has changed so that it is now very close to the XQuery model. Tree construction is described bottom up, and nodes are accessible before they have been attached to a parent node. Most users will not notice the change, but the difference affects many minor details, such as the way in which new nodes are validated against schema types and the way that namespaces work. It also means that some care is needed with absolute path expressions. For example, if the root of a tree is an "a" element with "b" children, then to select the "b" elements, you need to write /b , rather than /a/b as you might expect. The syntax /a/b (which is short for root(.)/child::a/child::b ) only works where the element named "a" is the child of the root node.

Functions and Named Templates

XQuery and XSLT 2.0 both offer the ability to define functions. Again, at first sight the facilities are not very different. In XQuery we can reverse a sequence with the recursive function:

 define function reverse ($seq as item()*) as item()* {   if (count($seq) < 2)    then $seq    else (reverse(subsequence($seq, 2)), $seq[1]) } 

In XSLT 2.0 we can write the same function as follows :

 <xslt:function name="reverse" as="item()*">   <xslt:param name="seq" as="item()*"/>   <xslt:sequence as="item()*" select="        if (count($seq) &lt; 2)         then $seq         else (reverse(subsequence($seq, 2)), $seq[1])"/> </xslt:function> 

Not a big difference. However, the fact that XSLT is a two-language system again creates differences. Because XSLT has one language for constructing nodes in the result tree and a different language (XPath) for selecting nodes from the source tree, it effectively has two mechanisms for defining what logically are functions: xslt:function for functions that can be called from XPath expressions and that return values; xslt:template for subroutines that can be called from the XSLT level and that write nodes to the result tree. XQuery only has a single language, so its expressions are more composable , which means that it gets by with a single function mechanism.

FLWOR Expressions

In the same way as the core construct of XPath is the path expression, at the center of the XQuery language lies the FLWOR expression. FLWOR expressions perform the same role in XQuery as the SELECT statement in SQL; and indeed, the SQL SELECT statement and its successors in various post-relational languages had a powerful influence on this language construct.

Nevertheless, a FLWOR expression contains nothing that does not translate directly into XSLT. The for part translates directly into xslt:for-each , the let part into xslt:variable , the where part into xslt:if , and the order by clause into xslt: sort .

FLWOR expressions are generally understood in very declarative terms, being based on the operations of the relational calculus such as Cartesian products, selection, and projection. By contrast, the equivalent XSLT constructs are often understood in procedural terms: People think of xslt:for-each as the analog of a loop in a procedural programming language. But beneath the surface, after cutting through the formal language used in explaining the semantics, there is very little real difference in functionality.

It's probably true that most XSLT processors actually evaluate xslt:for-each instructions using a procedural approach very similar to the surface model: Certainly , Saxon does so. Where two xslt:for-each instructions are nested, they are likely to be implemented by means of a nested loop.

With FLWOR expressions, the implementation is much more likely to use the repertoire of optimization techniques developed for relational databases, which can involve considerable rearrangement of the order of execution.

But these are not differences caused by the semantics of the language. They arise because XQuery is used in a different environment: that of searching large persistent databases. In this environment, the efficiency of a query depends on finding preconstructed indexes that give fast access to particular selections appearing in a query. The thrust of XQuery optimization is therefore to rewrite the query to make optimum use of indexes. XSLT processors don't generally have this luxury: There are no preconstructed indexes. The range of access paths available is much more limited; consequently, the opportunities for performance advantages from rewriting the query are also limited.

The difference in approach also arises because of XSLT's focus on handling document-oriented XML, whereas XQuery emphasizes data-oriented XML. When handling documents, the order of both the source document and the result document are significant, and they are often the same. Sequential processing is therefore usually the best strategy. When handling data, order (certainly in the relational tradition) is often unimportant, and strategies that change the order of execution can have a high payoff.

Template Rules

One of the few really significant differences between XSLT and XQuery is XSLT's use of template rules: an event-driven approach that decouples the description of how to process individual elements in the source tree from any assumptions about the context in which these elements appear. XQuery has no corresponding facility, although it is possible to prove that this does not reduce the expressive capability of the language: Any xslt:apply-templates instruction can in principle be translated into a conditional expression that makes direct calls on an explicit template depending on the properties of the selected node.

What template rules provide is not extra functionality, but modularity and potential for change. They enable the stylesheet to make fewer assumptions about the structure of the source document, and therefore to be more resilient to changes in this structure. This, of course, is very important for handling document-oriented XML and much less significant for data-oriented XML.

The very strength of the event-driven approach of template rules is also the cause of its difficulties: It makes optimization difficult. While XSLT processors use all kinds of tricks to limit the number of patterns that each node is tested against, and may in the future use schema information to limit this choice still further, it is hard to optimize the stylesheet as a whole, as distinct from optimizing individual template rules, because so little is known statically about which rules will call which others. XSLT processors can afford the cost of matching template rules, because the most common action of a stylesheet is to process each node exactly once, in sequential order. XQuery cannot afford this cost, because the very feasibility of executing a query in a reasonable length of time depends on static analysis of the query to devise an execution strategy that only visits the minimum number of nodes. There is a real difference in requirements here, and in the application areas that the two languages are addressing, that leads to a genuine difference in design philosophy.

Axes

XQuery does not offer the full set of axes used in XPath path expressions: This is the only part of XPath that is not incorporated directly into XQuery. Specifically, XQuery does not offer the following and preceding axes, the following-sibling and preceding-sibling axes, or the ancestor axis. Why was this decision made? I find it difficult to explain this decision, because I argued against it, but the arguments on both sides are credible, and I will try to do them justice .

XPath approaches the selection of nodes in a tree in a way that some regard as navigational. I prefer to think of it as functional, because the word navigational has connotations of procedural algorithms, but the essence is that nodes are found by virtue of their relationship to other nodes: go up three, then left two, then down one. This approach is traditionally difficult to optimize. In databases, following the relational tradition, the approach is to select objects based on their properties, not by describing a route by which they can be reached. This makes some people from the relational tradition deeply suspicious of path expressions, and anxious about the difficulty of optimizing them.

Some XQuery implementers are also using database engines whose storage and indexing structures are finely tuned to relational data and relational queries. Adapting these engines to hierarchic data structures and recursive path expressions is not an easy task, and queries that fit well into the relational pattern are often likely to execute far more efficiently than those that do not.

It is true that any expression involving the axes that XQuery has omitted can be rewritten to avoid these axes. Take, for example, the query "find a figure element that is immediately followed by another figure without any intervening text or elements." In XPath we would write:

 //figure[following-sibling::node()[1][self::figure]] 

XQuery does not offer the following-sibling axis, so how can this query be expressed ? One way is the following:

 for $p in //*   for $f at $i in $p/figure    where $p/node()[$i+1][self::figure]    return $f 

While this seems highly convoluted to someone familiar with XPath, I am told it comes very naturally to people who think in SQL terms. Is it any easier to optimize than the XPath version? Not for a processor that is working on a tree model implemented in terms of a fully connected object model either in memory or in persistent storage (typically a native XML database such as Software AG's Tamino). [5] But for implementers who have flattened the tree into a tuple structure held in tabular storage, who knows ?

[5] See http://www.softwareag.com/tamino.

Perhaps it comes down to different views as to the importance of such queries. In document-oriented XML, ordering is essential, and objects are much more likely to be identified by their relationships to other objects than by their properties. In data-oriented XML, objects are most usually identified by their content. So I strongly suspect that the real underlying reason for this decision is the natural bias in the XQuery group towards data-oriented XML.

Type Strictness

In XSLT 1.0 and XPath 1.0, the type system was very weak, in the sense that very few types were defined, and most operations accepted arguments of any type. As with scripting languages like JavaScript, if you supplied the wrong kind of object, the system did its best to convert it to the required type. So you could add strings, and you could concatenate numbers , and you could compare a set of nodes to a number, and get an answer. Not always the answer you might have expected, but an answer. Very few operations would ever cause a run-time error.

There was a reason behind this. The primary purpose of XSLT was for translating XML into display formats such as HTML or PDF. It was originally expected that the work would usually be done client-side, in the browser. And the last thing you want to happen when rendering a document on a browser is to put up a message saying "error in stylesheet." If the stylesheet is totaling a set of numbers, and it finds that one of the fields in the source document is not numeric after all, it should put something on the screen, even if it's only asterisks , rather than falling over in a heap.

Another reason for this was the awareness that in the XML world, all data is ultimately text. XML is basically (as we often tend to forget) a way of adding markup to text to indicate its structure. Other datatypes such as integers and Booleans exist only insofar as we decide to carve them out of the raw text: They are abstractions, whereas the text is real. With this world view, it is very natural that any operations on data should implicitly convert what they are given into the datatype that they are designed to process.

The developers of XQuery, by contrast, always believed firmly that a rich type system should be at the core of the language. There are many excellent reasons why languages in general, and query languages in particular, have tended to adopt strong type systems. Since the early days of data bases, there has been the idea of separating the data description, or schema, from the data-manipulation, or query language. The data description reflects the common understanding of the data that is shared by the user community, even though each user is constructing different queries. It defines the types of objects that can be found in the database, and as such it naturally forms the type system for the query language, because a query that isn't expressed in terms of these types of objects doesn't make sense.

A query processor makes use of type information from the database schema in two significant ways. One use is for error detection. It is helpful to detect errors as early as possible, and it is certainly desirable to detect an error rather than returning wrong data. Every SQL user has had the experience of getting an answer to a query that is not actually the answer to the query they thought they were asking. This can never be completely prevented, but a good type system can detect many of the sillier mistakes in advance. The second reason is optimization. The more knowledge that the system has at compile-time, the more clever it can be in devising an efficient execution plan for the query, and knowing about the types of the objects handled by the query is a key part of the picture.

As it happens, the thinking in the XSL group on typing has evolved considerably since XSLT 1.0. This is partly because XSLT has proved to be of significance in a great many roles other than client-side web browsing, and partly because of the emergence of XML Schema as a powerful influence on the adoption of XML by big business, even if it is fiercely resisted by the SGML cognoscenti. The thinking in the XQuery camp has also evolved, with an increasing recognition that there is a legitimate need to handle semi-structured data. The process has been one of gradual convergence and of increasing recognition on both sides that there is a wide spectrum of requirements. But XSLT still has more of a foot in the "loose typing" camp than XQuery does, because there is still more interest in processing schema-less documents, and also because the design of the language, with its event-based template rules, makes it much harder to use static type information effectively, either for error reporting or for optimization. So it can be said that XQuery has been making tentative steps towards looser typing, while XSLT has been making tentative steps in the opposite direction.

The range of views in both working groups ensured that discussions of the type system accounted for a great deal of the time and effort spent in developing the two languages. Many users may fail to notice this, because the bottom line is that in both XSLT and XQuery, most users can safely ignore most of the complexities of the type system. For example, both languages have elaborate facilities for validating the output documents against a schema. Some XQuery designers have always aspired to make it possible to report a compile-time error when a query is incapable of producing output that conforms to the required schema, or even ”a stronger test ”to report an error when it is capable of producing output that does not conform to the required schema. I suspect, however, that many users will simply output untyped XML, and if they need to validate it, they will do this as a separate process once the query or transformation is complete.



XQuery from the Experts(c) A Guide to the W3C XML Query Language
Beginning ASP.NET Databases Using VB.NET
ISBN: N/A
EAN: 2147483647
Year: 2005
Pages: 102

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net