D Other Functions (Non-Normative)

This Appendix describes some sources of functions that fall outside the scope of the function library defined in this specification. It includes both function specifications and function implementations. Inclusion of a function in this appendix does not constitute any kind of recommendation or endorsement; neither is omission from this appendix to be construed negatively. This Appendix does not attempt to give any information about licensing arrangements for these function specifications or implementations.

D.1 XPath Functions Defined in Other W3C Recommendations

A number of W3C Recommendations make use of XPath, and in some cases such Recommmendations define additional functions to be made available when XPath is used in a specific host language.

D.1.1 Functions Defined in XSLT

The various versions of XSLT have all included additional functions intended to be available only when XPath is used within XSLT, and not in other host language environments. Some of these functions were originally defined in XSLT, and subsequently migrated into the core function library defined in this specification.

Generally, the reason that functions have been defined in XSLT rather than in the core library has been that they required additional static or dynamic context information.

XSLT-defined functions share the core namespace http://www.w3.org/2005/xpath-functions (but in XPath 1.0 and XSLT 1.0, no namespace was defined for these functions).

The conformance rules for XSLT 4.0 require implementations to support either XPath 3.0 or XPath 3.1. Some of the new functions in XPath 3.1, however, must be supported by all XSLT 4.0 implementations whether or not they implement other parts of XPath 3.1.

The following table lists all functions that have been defined in XSLT 1.0, 2.0, or 3.0, and summarizes their status.

Function name Availability
fn:accumulator-after XSLT 3.0 only
fn:accumulator-before XSLT 3.0 only
fn:available-system-properties XSLT 3.0 only
fn:collation-key Common to XSLT 3.0 and XPath 3.1
fn:copy-of XSLT 3.0 only
fn:current XSLT 1.0, 2.0, and 3.0
fn:current-group XSLT 2.0 and 3.0
fn:current-grouping-key XSLT 2.0 and 3.0
fn:current-merge-group XSLT 3.0 only
fn:current-merge-key XSLT 3.0 only
fn:current-output-uri XSLT 3.0 only
fn:document XSLT 1.0, 2.0, and 3.0
fn:element-available XSLT 1.0, 2.0, and 3.0
fn:format-date XSLT 2.0; migrated to XPath 3.0 and 3.1
fn:format-dateTime XSLT 2.0; migrated to XPath 3.0 and 3.1
fn:format-number XSLT 1.0 and 2.0; migrated to XPath 3.0 and 3.1
fn:format-time XSLT 2.0; migrated to XPath 3.0 and 3.1
fn:function-available XSLT 1.0, 2.0, and 3.0
fn:generate-id XSLT 1.0 and 2.0; migrated to XPath 3.0 and 3.1
fn:json-to-xml Common to XSLT 3.0 and XPath 3.1
fn:key XSLT 1.0, 2.0, and 3.0
fn:regex-group XSLT 2.0 and 3.0
fn:snapshot XSLT 3.0 only
fn:stream-available XSLT 3.0 only
fn:system-property XSLT 1.0, 2.0, and 3.0
fn:type-available XSLT 2.0 and 3.0
fn:unparsed-entity-public-id XSLT 2.0 and 3.0
fn:unparsed-entity-uri XSLT 1.0, 2.0, and 3.0
fn:unparsed-text XSLT 2.0; migrated to XPath 3.0 and 3.1
fn:xml-to-json Common to XSLT 3.0 and XPath 3.1
map:contains Common to XSLT 3.0 and XPath 3.1
map:entry Common to XSLT 3.0 and XPath 3.1
map:find Common to XSLT 3.0 and XPath 3.1
map:for-each Common to XSLT 3.0 and XPath 3.1
map:get Common to XSLT 3.0 and XPath 3.1
map:keys Common to XSLT 3.0 and XPath 3.1
map:merge Common to XSLT 3.0 and XPath 3.1
map:put Common to XSLT 3.0 and XPath 3.1
map:remove Common to XSLT 3.0 and XPath 3.1
map:size Common to XSLT 3.0 and XPath 3.1

D.1.2 Functions Defined in XForms

XForms 1.1 is based on XPath 1.0. It adds the following functions to the set defined in XPath 1.0, using the same namespace:

boolean-from-string, is-card-number, avg, min, max, count-non-empty, index, power, random, compare, if, property, digest, hmac, local-date, local-dateTime, now, days-from-date, days-to-date, seconds-from-dateTime, seconds-to-dateTime, adjust-dateTime-to-timezone, seconds, months, instance, current, id, context, choose, event.

XForms 2.0 was first published as a W3C Working Draft, and subsequently as a W3C Community Group specification. These draft specifications do not include any additional functions beyond those in the core XPath specification.

D.1.3 Function Defined in XQuery Update 1.0

The XQuery Update 1.0 specification defines one additional function in the core namespace http://www.w3.org/2005/xpath-functions, namely fn:put. This function can be used to write a document to external storage. It is thus unusual in that it has side-effects; the XQuery Update 1.0 specification defines semantics for updating expressions including this function.

Although XQuery Update 1.0 is defined as an extension of XQuery 1.0, a number of implementors have adapted it, in a fairly intuitive way, to work with later versions of XQuery. At the time of this publication, later versions of the XQuery Update specification remain at Working Draft status.

D.2 Functions Defined by Community Groups

A number of community groups, with varying levels of formal organization, have defined specifications for additional function libraries to augment the core functions defined in this specification. Many of the resulting function specifications have implementations available for popular XPath, XQuery, and XSLT processors, though the level of support is highly variable.

The first such group was EXSLT. This activity was primarily concerned with augmenting the capability of XSLT 1.0, and many of its specifications were overtaken by core functions that became available in XPath 2.0. EXSLT defined a number of function modules covering:

Dates and Times
Dynamic XPath Evaluation
Common (containing most notably the widely used node-set function)
Math (max, min, abs, and trigonometric functions)
Random Number Generation
Regular Expressions
Sets (operations on sets of nodes including set intersection and difference)
String Manipulation (tokenize, replace, join and split, etc.)

Specifications from the EXSLT group can be found at [EXSLT].

A renewed attempt to define additional function libraries using XPath 2.0 as its baseline formed under the name EXPath. Again, the specifications are in various states of maturity and stability, and implementation across popular processors is patchy. At the time of this publication the function libraries that exist in stable published form include:

Binary (functions for manipulating binary data)
File Handling (reading and writing files)
Geospatial (handling of geographic data)
HTTP Client (sending HTTP requests)
ZIP Facility (reading and creating ZIP files or similar archives)

The EXPath community has also been engaged in other related projects, such as defining packaging standards for distribution of XSLT/XQuery components, and tools for unit testing. Its specifications can be found at [EXPath].

A third activity has operated under the name EXQuery, which as the name suggests has focused on extensions to XQuery. EXQuery has published a single specification, RestXQ, which is primarily a system of function annotations allowing XQuery functions to act as endpoints for RESTful services. It also includes some simple functions to assist with the creation of such services. The RestXQ specification can be found at [EXQuery].

D.3 The FunctX Library

Many useful functions can be written in XSLT or XQuery, and in this case the function implementations themselves can be portable across different XSLT and XQuery processors. This section describes one such library.

FunctX is an open-source library of general-purpose functions, supplied in the form of XQuery 1.0 and XSLT 2.0 implementations. It contains over a hundred functions. Typical examples of these functions are:

Test whether a string is all-whitespace
Trim leading and trailing whitespace
Test whether all the values in a sequence are distinct
Capitalize the first character of a string
Change the namespace of all elements in a tree
Get the number of days in a given month
Get the first or last day in a given month
Get the date of the preceding or following day
Ask whether an element has element-only, mixed, or simple content
Find the position of a node in a sequence
Count words in a string

The FunctX library can be found at [FunctX].

D.4 Illustrative user-written functions

Certain functions that were proposed for inclusion in this function library have been excluded on the basis that it is straightforward for users to implement these functions themselves using XSLT 2.0 or XQuery 1.0.

This Appendix provides sample implementations of some of these functions.

To emphasize that these functions are examples of functions that vendors may write, their names carry the prefix 'eg'. Vendors are free to define such functions in any namespace. A group of vendors may also choose to create a collection of such useful functions and put them in a common namespace.

D.4.1 eg:if-empty

In some situations, users may want to provide default values for missing information that may be signaled by elements that are omitted, have no value or have the empty sequence as their value. For example, a missing middle initial may be indicated by omitting the element or a non-existent bonus signaled with an empty sequence. This section includes examples of functions that provide such defaults. These functions return xs:anyAtomicType*. Users may want to write functions that return more specific types.

D.4.1.1 eg:if-empty
eg:if-empty(
$node as node(),
$value as xs:anyAtomicType
) as xs:anyAtomicType*

If the first argument is the empty sequence or an element without simple or complex content, eg:if-empty() returns the second argument; otherwise, it returns the content of the first argument.

XSLT implementation

<xsl:function name="eg:if-empty" as="xs:anyAtomicType*">
  <xsl:param name="node" as="node()?"/>
  <xsl:param name="value" as="xs:anyAtomicType"/>
  <xsl:sequence select="($node[child::node()], $value)[1]"/>
</xsl:function>

XQuery implementation

declare function eg:if-empty (
  $node as node()?,
  $value as xs:anyAtomicType) as xs:anyAtomicType* 
{
  ($node[child::node()], $value)[1]
}
                    
D.4.1.2 eg:if-absent
eg:if-absent(
$node as node(),
$value as xs:anyAtomicType
) as xs:anyAtomicType*

If the first argument is the empty sequence, eg:if-absent() returns the second argument; otherwise, it returns the content of the first argument.

XSLT implementation

<xsl:function name="eg:if-absent" as="xs:anyAtomicType*">
  <xsl:param name="node" as="node()?"/>
  <xsl:param name="value" as="xs:anyAtomicType"/>
  <xsl:sequence select="($node, $value)[1]"/>
</xsl:function>

XQuery implementation

declare function eg:if-absent (
  $node as node()?,
  $value as xs:anyAtomicType) as xs:anyAtomicType* 
{
  ($node, $value)[1]
}
                    

D.4.2 Union, intersection and difference on sequences of values

D.4.2.1 eg:value-union
eg:value-union(
$arg1 as xs:anyAtomicType*,
$arg2 as xs:anyAtomicType*
) as xs:anyAtomicType*

This function returns a sequence containing all the distinct items in $arg1 and $arg2, in an arbitrary order.

XSLT implementation

<xsl:function name="eg:value-union" as="xs:anyAtomicType*">
  <xsl:param name="arg1" as="xs:anyAtomicType*"/>
  <xsl:param name="arg2" as="xs:anyAtomicType*"/>
  <xsl:sequence
     select="fn:distinct-values(($arg1, $arg2))"/> 
</xsl:function>

XQuery implementation

declare function eg:value-union (
  $arg1 as xs:anyAtomicType*,
  $arg2 as xs:anyAtomicType*) as xs:anyAtomicType* 
{
  fn:distinct-values(($arg1, $arg2))
}
                    
D.4.2.2 eg:value-intersect
eg:value-intersect(
$arg1 as xs:anyAtomicType*,
$arg2 as xs:anyAtomicType*
) as xs:anyAtomicType*

This function returns a sequence containing all the distinct items that appear in both $arg1 and $arg2, in an arbitrary order.

XSLT implementation>

<xsl:function name="eg:value-intersect" as="xs:anyAtomicType*">
  <xsl:param name="arg1" as="xs:anyAtomicType*"/>
  <xsl:param name="arg2" as="xs:anyAtomicType*"/>
  <xsl:sequence 
     select="fn:distinct-values($arg1[.=$arg2])"/>
</xsl:function>

XQuery implementation

declare function eg:value-intersect (
  $arg1 as xs:anyAtomicType*,
  $arg2 as xs:anyAtomicType* ) as xs:anyAtomicType* 
{
  fn:distinct-values($arg1[.=$arg2])
}
                    
D.4.2.3 eg:value-except
eg:value-except(
$arg1 as xs:anyAtomicType*,
$arg2 as xs:anyAtomicType*
) as xs:anyAtomicType*

This function returns a sequence containing all the distinct items that appear in $arg1 but not in $arg2, in an arbitrary order.

XSLT implementation

<xsl:function name="eg:value-except" as="xs:anyAtomicType*">
  <xsl:param name="arg1" as="xs:anyAtomicType*"/>
  <xsl:param name="arg2" as="xs:anyAtomicType*"/>
  <xsl:sequence
     select="fn:distinct-values($arg1[not(.=$arg2)])"/>
</xsl:function>

XQuery implementation

declare function eg:value-except (
  $arg1 as xs:anyAtomicType*,
  $arg2 as xs:anyAtomicType*) as xs:anyAtomicType* 
{
  fn:distinct-values($arg1[not(.=$arg2)])
}

D.4.3 eg:index-of-node

eg:index-of-node(
$seq as node()*,
$search as node()
) as xs:integer*

This function returns a sequence of positive integers giving the positions within the sequence $seq of nodes that are identical to $search.

The nodes in the sequence $seq are compared with $search under the rules for the is operator. If a node compares identical, then the position of that node in the sequence $seq is included in the result.

If the value of $seq is the empty sequence, or if no node in $seq matches $search, then the empty sequence is returned.

The index is 1-based, not 0-based.

The result sequence is in ascending numeric order.

XSLT implementation

<xsl:function name="eg:index-of-node" as="xs:integer*">
  <xsl:param name="seq" as="node()*"/>
  <xsl:param name="search" as="node()"/>
  <xsl:sequence select="filter(
      1 to count($seq),
      function($i as xs:integer) as xs:boolean {$seq[$i] is $search}  
    )
  "/>
</xsl:function>

XQuery implementation

declare function eg:index-of-node($seq as node()*, $search as node()) as xs:integer* 
{
    fn:filter(
      1 to fn:count($seq),
      function($i as xs:integer) as xs:boolean {$seq[$i] is $search}   
    )

}

An alternative implementation, which might be faster in systems where indexing into a sequence is slow, is:

declare function eg:index-of-node($seq as node()*, $search as node()) as xs:integer* 
{
  fn:for-each-pair(
     $seq, 1 to fn:count($seq),
     function($node, $index) {
        if($node is $search) then $index else () 
     })
}

D.4.4 eg:string-pad

eg:string-pad(
$padString as xs:string,
$padCount as xs:integer
) as xs:string

Returns a xs:string consisting of a given number of copies of an xs:string argument concatenated together.

XSLT implementation

<xsl:function name="eg:string-pad" as="xs:string">
  <xsl:param name="padString" as="xs:string?"/>
  <xsl:param name="padCount" as="xs:integer"/>
  <xsl:sequence select="
     fn:string-join(1 to $padCount ! $padString)"/>
 </xsl:function>
                

XQuery implementation

declare function eg:string-pad (
  $padString as xs:string?,
  $padCount as xs:integer) as xs:string 
{
   fn:string-join(1 to $padCount ! $padString)
}
                

This returns the zero-length string if $padString is the empty sequence, which is consistent with the general principle that if an xs:string argument is the empty sequence it is treated as if it were the zero-length string.

D.4.5 eg:distinct-nodes-stable

eg:distinct-nodes-stable(
$arg as node()*
) as node()*

This function illustrates one possible implementation of a distinct-nodes function. It removes duplicate nodes by identity, preserving the first occurrence of each node.

XPath

$arg[empty(subsequence($arg, 1, position()-1) intersect .)]
                

XSLT implementation

<xsl:function name="eg:distinct-nodes-stable" as="node()*">
  <xsl:param name="arg" as="node()*"/>
  <xsl:sequence select=""
    fn:fold-left(
      $arg, (),
      function($foundSoFar as node()*, $this as node()) as node()* {
        if ($foundSoFar intersect $this)
        then $foundSoFar
        else ($foundSoFar, $this)
      })
  "/> 
</xsl:function>
                

XQuery implementation

declare function eg:distinct-nodes-stable ($arg as node()*) as node()* { 
  fn:fold-left(
      $arg, (),
      function($foundSoFar as node()*, $this as node()) as node()* {
        if ($foundSoFar intersect $this)
        then $foundSoFar
        else ($foundSoFar, $this)
      })

};