The XPath 2.0 Sequence Functions

There are plenty of functions that work with sequences; here's an overview:

  • fn:zero-or-one returns the sequence you pass to it if it contains zero or one items. Otherwise it raises an error.

  • fn: one-or-more returns the sequence you pass to it if it contains one or more items. Otherwise it raises an error.

  • fn: exactly-one returns the sequence you pass to it if it contains exactly one item. Otherwise it raises an error.

  • fn:boolean casts a sequence to an xs:boolean value.

  • fn:index-of returns a sequence of xs:integer values, each of which is the index of a item in the sequence given as the first argument that is equal to the value of the second argument.

  • fn:empty indicates whether the passed sequence is empty.

  • fn:exists indicates whether the passed sequence is not empty.

  • fn: distinct-values returns a sequence from which duplicate values have been removed.

  • fn: insert-before inserts an item or sequence of items into a sequence at a specified position.

  • fn:remove removes an item from the given position in a sequence.

  • fn: subsequence returns the subsequence of a given sequence. You pass the location at which to start the subsequence and, optionally , the length of the sequence.

  • fn:unordered indicates that the given sequence may be returned in any order.

  • fn: deep-equal returns true if the two passed sequences have items that compare equal in corresponding positions .

  • fn:count returns the number of items in the passed sequence.

  • fn:avg returns the average value of a sequence of values.

  • fn:max returns the maximum value from a sequence of comparable items.

  • fn:min returns the minimum value from a sequence of comparable items.

  • fn:sum returns the sum of a sequence of values.

  • fn:id returns the sequence of nodes having IDs that match IDREF values in the passed sequence.

  • fn:idref returns the sequence of nodes with IDREF values matching the ID values in the argument sequence.

  • fn:doc returns a document node retrieved from the given URI.

  • fn:collection returns a sequence of document nodes retrieved from the given URI.

Sequences are new in XPath 2.0, and as you can see here, there's a rich set of functions to work with them. We'll take this list apart, function by function, in the remainder of this chapter, starting with the fn:zero-or-one function.

The fn:zero-or-one Function

This function returns $srcval if it is a sequence that holds zero or one items only. Here's how it works:

 
 fn:zero-or-one(  $srcval  as item*) as item? 

This function does not handle the case where you've passed a sequence with more than zero or one items gracefully. Instead, it causes an error with this text: "fn:zero-or-one called with a sequence containing more than one item".

The fn:one-or-more Function

This function returns $srcval if it is a sequence that holds one or more items only. Here's how you use this function:

 
 fn:one-or-more(  $srcval  as item*) as item+ 

This function does not handle the case where you've passed a sequence with zero items well; in this case, it causes an error with this text: "fn:one-or-more called with a sequence containing zero items".

The fn:exactly-one Function

This function returns $srcval if it contains exactly one item:

 
 fn:exactly-one(  $srcval  as item*) as item 

If you call this function with a sequence containing zero or more than one item, you'll get an error with the text "fn:exactly-one called with a sequence containing zero or more than one item".

SHOP TALK : EXCEPTIONS WITHOUT ERRORS

At this point in the development of the XPath 2.0 specification, it's a little difficult to see how the fn:zero-or-one , fn:one-or-more , and fn:exactly-one functions are intended to be used. No examples are provided in the XQuery 1.0 and XPath 2.0 Functions and Operators document.

If these functions are intended as simple tests, they'd probably be better implemented to return a Boolean valuesomething like this, where we're testing to avoid returning the empty sequences:

 
 if(fn:one-or-more($seq)) then $seq else (1, 2, 3) 

Instead, these functions either return simply the sequence you pass them, or cause an error. And the way these functions are written makes it look as though causing that error may be considered standard procedure.

Ever since the (very useful) introduction of try/catch blocks in languages like C++ and Java, programming language developers have become somewhat lax about the use of errors. This laxness has gotten to the point where runtime errors are being treated as a programming device rather than a recovery mechanisminstead of handling runtime errors as exceptions and trying to recover from them, language elements now routinely throw exceptions during normal processing as part of their standard operation.

However, there's considerable backlash against this kind of practice now, which abuses the exception-handling mechanisms built into programming languages. We'll see how these functions develop as XPath 2.0 develops, and whether they stay as they are or become more standard test functions instead.


The fn:boolean Function

This function returns the effective Boolean value of a sequence:

 
 fn:boolean(  $srcval  as item*) as xs:boolean 

This function returns false if $srcval is the empty sequence. If $srcval is an atomic value, it returns false if $srcval is the singleton xs:boolean value false , "", a numeric value equal to zero, a singleton xs:double , or an xs:float value that is NaN.

Otherwise, this function returns a value of true .

The fn:index-of Function

This function returns a sequence of positive integers giving the position(s) in a sequence of an item. Here's how you can use fn:index-of :

 
 fn:index-of(  $seq  as xs:anyAtomicType*,  $srchParam  as xs:anyAtomicType) as xs:integer* fn:index-of(  $seq  as xs:anyAtomicType*,  $srchParam  as xs:anyAtomicType,  $collation  as xs:string) as xs:integer* 

In this case, $seq is a sequence to search, $srchParam is the item you're searching for, and $collation represents an (optional) collation.

This function lets you search sequences for multiple matcheshere are a few examples:

 
 fn:index-of((2, 3, 4), 2) returns 1. fn:index-of((2, 3, 4), 3) returns 2. fn:index-of((2, 3, 4), 5) returns (). fn:index-of((2, 3, 3, 4), 3) returns (2, 3). fn:index-of((2, 3, 3, 3, 4), 3) returns (2, 3, 4). 

The fn:empty Function

This function tests for the empty sequence:

 
 fn:empty(  $srcval  as item*) as xs:boolean 

If $srcval is the empty sequence, this function returns true ; otherwise, this function returns false .

For example, fn:empty(()) returns true .

The fn:exists Function

The fn:exists function returns true if you pass it a sequence that is not the empty sequence, and false otherwise. Here's how you use it:

 
 fn:exists(  $srcval  as item*) as xs:boolean 

Here are a few examples:

 
 fn:exists(()) returns false. fn:exists(1) returns true. fn:exists((1, 2, 3)) returns true. 

This function is a useful and quick one to make sure you're not dealing with an empty sequence.

The fn:distinct-values Function

You use fn:distinct-values to strip duplicate values from a general sequence:

 
 fn:distinct-values(  $srcval  as xs:anyAtomicType*) as xs:anyAtomicType* fn:distinct-values(  $srcval  as xs:anyAtomicType*,  $collation  as xs:string) as xs:anyAtomicType* 

Note that you can specify a collation with this function if you want to.

Here's an example; in this case, we're going to extract the distinct values from the sequence (1, 2, 2, 3) using fn:distinct-values , as you can see in ch12_04.xsl (Listing 12.4).

Listing 12.4 Using fn:distinct-values ( ch12_04.xsl )
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">  <xsl:template match="/">   Here are the distinct values:   <xsl:value-of select="distinct-values((1, 2, 2, 3))"   separator=", "/>   </xsl:template>  </xsl:stylesheet> 

And here's the resultas you can see, we've been able to get the distinct values from the sequence:

 
 <?xml version="1.0" encoding="UTF-8"?> Here are the distinct values: 1, 2, 3 

The fn:insert-before Function

As you can guess from its name , the fn:insert-before function lets you insert one sequence into another. Here's how you use this function:

 
 fn:insert-before(  $target  as item*,  $position  as xs:integer,  $inserts  as item*) as item* 

This is the first of the sequence-editing functions that we'll cover. In this case, this function inserts the sequence $inserts into the $target sequence, starting at $position , and returns the results.

Note that there is no "fn:insert-after" function.

Here are a few examples:

 
 fn:insert-before((1, 2, 3), 1, 4) returns (4, 1, 2, 3) fn:insert-before((1, 2, 3), 2, 4) returns (1, 4, 2, 3) fn:insert-before((1, 2, 3), 3, 4) returns (1, 2, 4, 3) fn:insert-before((1, 2, 3), 4, 4) returns (1, 2, 3, 4) fn:insert-before((1, 2, 3), 4, (4, 5, 6)) returns (1, 2, 3, 4, 5, 6) fn:insert-before((1, 2, 3), 2, (4, 5, 6)) returns (1, 4, 5, 6, 2, 3) 

As you can see, this powerful function lets you put your own sequences together from other sequences.

The fn:remove Function

This function simply removes an item from a sequence and returns the resulting sequence:

 
 fn:remove(  $srcval  as item*,  $position  as xs:integer) as item* 

The fn:remove function removes the item in $srcval at position $position and returns the resulting sequence. There's no provision here for removing more than one item.

For example, we can convert the sequence (1, 2, 2, 3) to (1, 2, 3) using fn:remove , as you see in ch12_05.xsl (Listing 12.5).

Listing 12.5 Using fn:remove ( ch12_05.xsl )
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">  <xsl:template match="/">   Here is the result:   <xsl:value-of select="remove((1, 2, 2, 3), 3)"   separator=", "/>   </xsl:template>  </xsl:stylesheet> 

And here's the result:

 
 <?xml version="1.0" encoding="UTF-8"?> Here is the result: 1, 2, 3 

The fn:subsequence Function

The fn:subsequence function lets you extract a subsequence from a sequence. Here's how you use this function:

 
 fn:subsequence(  $srcval  as item*,  $start  as xs:double) as item* fn:subsequence(  $srcval  as item*,  $start  as xs:double,  $length  as xs:double) as item* 

This function is great when you want to carve a subsequence out of a sequence. Here are a few examples:

 
 fn:subsequence((1, 2, 3, 4), 2) returns (2, 3, 4) fn:subsequence((1, 2, 3, 4), 3) returns (3, 4) fn:subsequence((1, 2, 3, 4), 2, 2) returns (2, 3) 

The fn:unordered Function

You pass this function a sequenceor an expression that yields a sequenceand this function indicates that the resulting sequence may be returned in any order. Here's how you use this function:

 
 fn:unordered(  $srcval  as item*) as item* 

The fn:deep-equal Function

This function compares two sequences and returns a Boolean value:

 
 fn:deep-equal(  $parameter1  as item*,  $parameter2  as item*) as xs:boolean fn:deep-equal(  $parameter1  as item*,  $parameter2  as item*,  $collationLiteral  as string) as xs:boolean 

For this function to return a value of true , the two sequences must have the same values, which means that they have the same number of items and that items in corresponding positions in the two sequences must compare equal. Otherwise, the function returns false .

Here are some examples:

 
 fn:deep-equal((1, 2, 3), (1, 2, 3, 4)) returns false. fn:deep-equal((1, 2, 3), (1, 2, 3)) returns true . 

The fn:count Function

The fn:count function works much the same way as the XPath 1.0 aggregate function count . In XPath 2.0, fn:count returns the number of items in a sequence. Here's how you use it:

 
 fn:count(  $srcval  as item*) as xs:integer 

For example, fn:count((1, 2, 3, 4)) returns 4, fn:count(()) returns 0, and fn:count((1, 2), (3, 4, 5)) returns 5.

The fn:avg Function

This function averages the numeric values (as well as yearMonthDuration and dayTimeDuration values) you pass to it and returns the result:

 
 fn:avg(  $srcval  as xdt:anyAtomicType*) as xdt:anyAtomicType? 

This function just evaluates sum( $srcval ) div count( $srcval )) and returns the result.

You can see an example in ch12_06.xsl (Listing 12.6), where we're averging the values 1, 2, 3, and 4.

Listing 12.6 Using fn:avg ( ch12_06.xsl )
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">  <xsl:template match="/">   The average value is <xsl:value-of select="avg((1, 2, 3, 4))"/>.   </xsl:template>  </xsl:stylesheet> 

And here's the result:

 
 <?xml version="1.0" encoding="UTF-8"?> The average value is 2.5. 

The fn:max Function

The fn:max function returns the maximum value of a sequence of values:

 
 fn:max(  $srcval  as xdt:anyAtomicType*) as xdt:anyAtomicType? fn:max(  $srcval  as xdt:anyAtomicType*,  $collation  as string) as xdt:anyAtomicType? 

Note that you can also include a collation when calling this function.

This function works as you'd expecthere are a few examples:

 
 fn:max((1, 2, 3)) returns 3. fn:max((1, 2, 1)) returns 2. fn:max((6, 5, 4, 3, 2, 1)) returns 6. 

The fn:min Function

As you can tell from this function's name, fn:min returns the minimum value from a sequence of values. Here are the signatures for this function:

 
 fn:min(  $srcval  as xdt:anyAtomicType*) as xdt:anyAtomicType? fn:min(  $srcval  as xdt:anyAtomicType*,  $collation  as string) as xdt:anyAtomicType? 

Here are some examples:

 
 fn:min((1, 2, 3)) returns 1. fn:min((7, 2, 1)) returns 1. fn:min((6, 5, 4, 3, 2)) returns 2. 

The fn:sum Function

This function returns a sum of the values that you pass it in a sequence:

 
 fn:sum($srcval as xdt:anyAtomicType*) as xdt:anyAtomicType? 

You can see an example in ch12_07.xsl (Listing 12.7), where we're using this function to add up the total masses in our planetary data document.

Listing 12.7 Using fn:sum ( ch12_07.xsl )
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">  <xsl:template match="/">   The total mass is <xsl:value-of select="sum(//planet/mass)"/>.   </xsl:template>  </xsl:stylesheet> 

And here's the result:

 
 <?xml version="1.0" encoding="UTF-8"?> The total mass is 1.8703. 

The fn:id Function

This function lets you retrieve element nodes with specified ids; here's how you use it:

 
 fn:id(  $srcval  as xs:string*) as element* 

In this case, $srcval is of type xs:string and is treated as if it were of type xs:IDREFS , that is, as a space-separated sequence of tokens, each of which is interpreted as an xs:IDREF . The function returns a sequence of those elements that have an ID value equal to one or more of the IDREFs in the list of IDREFs (the ID values are set using attributes of type xs:ID ).

The fn:idref Function

This function works in much the opposite way as the fn:id functionit returns all the nodes with IDREF values that reference one or more of the IDs you pass to this function. Here's how it works:

 
 fn:idref(  $srcval  as xs:string*) as node* 

In this case, $srcval is treated as if it were a space-separated sequence of tokens, each interpreted as an ID.

The fn:doc Function

The fn:doc function is an interesting function that lets you read in entire documents. You just supply the URI of the document:

 
 fn:doc(  $uri  as xs:string?) as document? 

Note that $uri here must be an xs:string that obeys the restrictions for xs:anyURI. When you use this function, it will return a valid document node from the new document you're reading in, unless the URI cannot be resolved.

Say that you wanted to work with an entirely new document in the middle of processing another documentfor example, you may want to fetch some crucial data from that new document that will aid in processing the current document. Being able to work with multiple documents in this way is powerful.

Here's an example; in this case, we'll read in our planetary data document and extract data from it. In this case, that document will be in the same directory as we're already working in, so we can load in that document using the fn:doc function this way in Saxon:

 
 <xsl:template match="/">  That planet is <xsl:value-of select="doc('ch02_01.xml')..."/>.  </xsl:template> 

Now we have a document node for the target document, and can apply XPath expressions to it as you see in ch12_08.xsl (Listing 12.8), where we're extracting the name of the first planet in the document, which is Mercury.

Listing 12.8 Using fn:doc ( ch12_08.xsl )
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">  <xsl:template match="/">   That planet is <xsl:value-of select="doc('ch02_01.xml')//planet[1]/name"/>.   </xsl:template>  </xsl:stylesheet> 

And here's the result you get when you run this through Saxon:

 
 <?xml version="1.0" encoding="UTF-8"?> That planet is Mercury. 

It's often more useful to load a new document into its own variable and work with that variable, which will be accessible throughout an XSLT document. For example, here's how that might look in this case, where we're assigning the planetary data document to a variable named $document :

 
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">  <xsl:variable name="document" select="doc('ch02_01.xml')"/>   .   .  . 

Now we're free to use that variable in our XPath 2.0 expressions, like this:

 
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">     <xsl:variable name="document" select="doc('ch02_01.xml')"/>     <xsl:template match="/">  That planet is <xsl:value-of select="$document//planet[1]/name"/>.  </xsl:template> </xsl:stylesheet> 

You can also use the fn:doc function to compare two documents, using the is operator. You can see a simple example of this in ch12_09.xsl (Listing 12.9), where we're just comparing a document to itself.

Listing 12.9 Using fn:doc to Compare Documents ( ch12_09.xsl )
 <xsl:stylesheet version="2.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"     xmlns:xs="http://www.w3.org/2001/XMLSchema">     <xsl:template match="/">  <xsl:value-of select="if(doc('ch02_01.xml') is doc('ch02_01.xml'))   then 'The documents are the same.'   else 'The documents are not the same.'"/>   </xsl:template>  </xsl:stylesheet> 

Here's the resultnot surprisingly, when you compare a document to itself, the comparison reveals that the compared documents are the same:

 
 <?xml version="1.0" encoding="UTF-8"?> The documents are the same. 

The fn:collection Function

This function is designed to let you work with collections of nodes, and here's how you use it:

 
 fn:collection(  $srcval  as xs:string) as node* 

You pass it an xs:string value that is restricted to legal values for xs:anyURI and this function will return a document node from that URI, unless there's been an error. This function is not implemented in Saxon yet.

That completes our look at the node and sequence functionsand that completes our book on XPath 1.0 and 2.0.

We've come far in this book and covered a great deal, from the most basic up through many advanced topics. All that's left is to put all this technology to work for yourself!



XPath. Navigating XML with XPath 1.0 and 2.0 Kick Start
XPath Kick Start: Navigating XML with XPath 1.0 and 2.0
ISBN: 0672324113
EAN: 2147483647
Year: 2002
Pages: 131

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