XML Path Language (XPath) Higher Order Functions
December 5, 2023
XML Path Language (XPath) Higher Order Functions (HOF) in context of XSLT Language
1) Introduction
XSLT (XSL Transformations) is a template and rule based language, for transforming XML and text documents into other XML, HTML and text documents. XPath (XML Path Language) is an expression language, where an XPath path expression is used for hierarchic addressing of the nodes in an XML tree. The XPath language is very often used within XSLT language stylesheets. An XSLT stylesheet is a term used to refer to XML and text transformation programs written with XSLT language.
XSL is a family of W3C language recommendations for defining XML document transformation and presentation.
Within this article we shall discuss in detail the topic of XPath version 3.1's (this shall be subsequently referred to as XPath 3.1 within this article) "Higher Order Functions (HOF)" feature when used within XSLT 3.0 language stylesheets.
HOFs are not mentioned explicitly as a separate concept within the XPath 3.1 specification. But they are explained and defined within the "XPath and XQuery Functions and Operators (F&O) 3.1" specification (please see https://www.w3.org/TR/xpath-functions-31/#higher-order-functions) (and are therefore a core XPath language feature. The XPath and XQuery languages share the common functions and operators as referred with the preceding mentioned web link). The XPath HOFs are just like any other XPath functions (i.e, HOFs like any other functions accept zero or more arguments, have a body of language instructions and return a result value).
The XPath F&O specification defines HOFs as following: Functions that accept functions among their arguments, or that return functions in their result.
The related Wikipedia article (please refer to the "5) References" section of this article) defines HOFs as following: A higher-order function (HOF) is a function that does at least one of the following:
a) Takes one or more functions as arguments (i.e. a procedural parameter, which is a parameter of a procedure that is itself a procedure).
b) Returns a function as its result.
Both of the above cited definitions of HOFs mean the same.
The XPath HOFs are just like any other XPath functions, in the sense that they accept zero or more arguments, have a function body and they return a value as a result of function body's evaluation. But since an XPath HOF "accepts or returns" (one or more) function definition(s), an HOF's function body can invoke the function passed to an HOF or the caller of HOF may make use of function (for e.g, calling the returned function or passing the returned function to another XPath evaluation context) returned by the HOF. The ability to pass a function definition as a function argument, and to return a function definition as a result of function call, provides the calling code for the HOFs a different and a useful kind of function definition abstraction. These HOF characteristics make HOF more generic than non HOF functions (i.e, a particular HOF has more implementation behavior than a corresponding relevant non HOF function. An HOF's function body code shall usually make a function call [among other code within an HOF] to a function that was passed as an argument to an HOF. Since an HOF's one or more function argument represents any relevant XPath function definition, the implementation behavior of an HOF shall vary fundamentally depending on the function passed as an argument to HOF.).
We shall discuss the XPath HOFs concepts in greater detail further within this article. Firstly, we shall discuss within this article in following sections, a few of the XSLT and XPath language features (like the concepts of XSLT and XPath language callable components) that may make use of XPath HOFs, which shall lay for the purpose of this article the XSLT and XPath language context for the specific topic of XPath HOFs which is the main theme of this article.
For the purposes of this article we shall be using the terms XSLT and XPath to refer to 3.0 version of XSLT language and 3.1 version of XPath language respectively, unless we write otherwise within the text of this article.
2) XSLT 3.0 and XPath 3.1 callable components
The main theme of this article is XPath "Higher Order Functions" (HOF). The XPath HOFs are programming callable components, available for use within XPath expressions. We shall describe within this section what do we mean by programming language callable components, within the context of XSLT and XPath languages, and we shall also describe within this section with suitable examples the various kinds of programming language callable components provided by XSLT and XPath languages.
The programming language features like functions and procedures are often used to produce modularity within the source code. The source code which is modular is easy to understand, and also to modify when requirements change or when bugs need to be fixed within the source code. By virtue of codebase modularity, we have reusable pieces of code within a program as a whole (which helps to manage codebase complexity, amidst writing and maintaining the source code). The programming language features like functions and procedures, are the callable components within a software program. A particular programming language callable component is essentially invoked/called from one or more places within the source code. In general, a programming language callable component has following runtime characteristics: a programming language callable component is identified by a name, they have zero or more parameters (the code calling a callable component passes arguments corresponding to the callable component's parameters), they have a body of code that is evaluated/executed when the callable component is called (the body of code of a callable component shall use the argument values, if they were passed during the call to a callable component), and a callable component often returns a value that the caller of callable component may use for specific business codebase logic.
The following sections describe the often used and most important XSLT and XPath callable components.
2.1 XSLT callable components
The following subsections describe in some detail about the various kinds of XSLT callable components.
2.1.1 XSLT template rules
An "XSLT template rule" contains a sequence of XSLT language instructions enclosed within XML tags
<xsl:template> and </xsl:template>. The XSLT templates are meant to be invoked
for "XPath Data Model (XDM)" items that match a particular pattern (a pattern in
this context, could represent XML element names or their values, XML attribute
names or their values, values of XML text nodes etc. An XSLT template rule
pattern is defined by the value of xsl:template
element's "match" attribute).
The XDM items are of following kinds: Nodes (these may be of various kinds. The
most important ones are: XML document, element, attribute and text nodes),
atomic values (these could be for e.g, string values, integer values, date
values, time values, duration values etc) and function items (a function item
is a compiled representation of an XPath inline function expression [which is a
syntax to define XPath user defined functions], where
these functions may be called from within XPath expressions).
An XSLT template rule may optionally have one or
more xsl:param
elements at the beginning of their stylesheet transformation
content. The template rule's xsl:param
elements are parameter definitions
of the template rule callable component. These template rule parameters receive
values via template rule's invocation via the xsl:apply-templates
instruction
(these codebase characteristics of XSLT template rules make template rules
to behave like programming language subroutines, procedures or the callable
components).
The XSLT template rule parameters provide functionality similar to XSLT variables, in the sense that XSLT template parameters
may be dereferenced/accessed by their names within the template rule body.
Following is an XSLT stylesheet example [XSL1] that uses the template rules:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/info">
<result>
<xsl:apply-templates select="val"/>
</result>
</xsl:template>
<xsl:template match="val">
<val id="{@id}" contentLength="{string-length(.)}">
<xsl:value-of select="."/>
</val>
</xsl:template>
</xsl:stylesheet>
The above mentioned XSLT stylesheet, when used to transform the following XML instance document [XML1],
<info>
<val id="1">hi</val>
<val id="2">hello</val>
<val id="3">there</val>
</info>
produces the following XML document transformation result.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<val id="1" contentLength="2">hi</val>
<val id="2" contentLength="5">hello</val>
<val id="3" contentLength="5">there</val>
</result>
Let's discuss briefly the XML document transformation logic of the XSLT stylesheet [XSL1] mentioned above.
This stylesheet has two user defined XSLT transformation rules. The XSLT user defined transformation rule identified by an attribute
match="/info"
is the starting point of XML document transformation within this stylesheet (this template rule is invoked only once during execution of this
XSLT stylesheet). An XSLT xsl:apply-templates
instruction within this stylesheet invokes the template rule having attribute match="val" repeatedly
for each XML sibling element "val". The XML document instance elements "val"
are selected in order by the xsl:apply-templates
instruction according to the order (known as an XML document order) in which the XML "val" elements are
available within an XML document instance.
The XSLT template rule match="val" transforms the XML input document element "val" by producing an additional XML attribute "contentLength" on the "val" element on the XSLT transformation output. The attribute "contentLength"'s value is an integer equal to the string length of string contents of element "val".
The 2.0 and higher versions of XSLT language have new features (that are not available with XSLT 1.0), related to stronger content type checking (which is also referred to with terms like type checking or data type checking) of XSLT template arguments and return values.
For example, we may have following XSLT stylesheet template rule as a modification of corresponding template rule match="val" (mentioned within the XSLT stylesheet [XSL1] as illustrated above), which requires an XSLT processor to check the content type of template's transformation result.
<xsl:template match="val" as="element(val)">
<val id="{@id}" contentLength="{string-length(.)}">
<xsl:value-of select="."/>
</val>
</xsl:template>
This template rule uses an attribute as="element(val)" (the value of attribute "as" is an XPath SequenceType
expression) on the xsl:template
element. Due to this, the XSLT processor shall
do a content type check, that whether the content produced by this template is
an XML element named "val". If the content produced by this template doesn't conform to the defined SequenceType (the value of xsl:template
element's "as"
attribute), the XSLT transformation shall fail with an error.
For example, if this template rule was written as following (this template now produces an XML element "val1" instead of "val"),
<xsl:template match="val" as="element(val)">
<val1 id="{@id}" contentLength="{string-length(.)}">
<xsl:value-of select="."/>
</val1>
</xsl:template>
the XSLT transformation fails with following error message (as produced by XalanJ XSLT 3.0 processor):
"XTTE0505: The required result type of template val is element(val). But the template result doesn't conform to this required type."
Other XSLT 3.0 processors produce similar error messages.
Specifying the XSLT "as" attribute on xsl:template
elements is particularly useful within large and complex XSLT stylesheet
transformations, where it may be difficult and error prone for XSLT stylesheet
authors to visually verify the content produced by XSLT template rules.
Let's discuss another XSLT stylesheet transformation
example within the following paragraphs, illustrating the use of XPath
SequenceType "as" attribute on stylesheet element xsl:template
.
XML instance document [XML2]:
<info>
<val id="1">hi</val>
<val id="2">hello</val>
<val id="3">there</val>
<val id="4">that's</val>
<val id="5">nice</val>
</info>
XSLT stylesheet document [XSL2]:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/info">
<result>
<xsl:apply-templates select="val"/>
</result>
</xsl:template>
<xsl:template match="val" as="element()">
<xsl:choose>
<xsl:when test="(@id mod 2) eq 0">
<valEven>
<xsl:copy-of select="@id"/>
<xsl:value-of select="."/>
</valEven>
</xsl:when>
<xsl:otherwise>
<valOdd>
<xsl:copy-of select="@id"/>
<xsl:value-of select="."/>
</valOdd>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When an XML instance document [XML2] is transformed by the stylesheet [XSL2] as mentioned above, the following XSLT transformation result is produced.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<valOdd id="1">hi</valOdd>
<valEven id="2">hello</valEven>
<valOdd id="3">there</valOdd>
<valEven id="4">that's</valEven>
<valOdd id="5">nice</valOdd>
</result>
Following are the key design concepts with respect to the XSLT transformation example [XSL2] as illustrated above.
1) When the XSLT template rule xsl:template match="val" is invoked for an XML input document element "val", this template produces an XML tranformation output result using the element "val"'s XML document structure and content. If an element "val"'s attribute "id"'s numeric value is even, an XML element "valEven" is emitted otherwise an XML element "valOdd" is emitted.
2) Since the XSLT transformation result of this template rule may be two different named XML elements, we have chosen xsl:template
's
attribute "as" to have SequenceType value element()
(denoting an XML element
having any name. The value element(*)
of attribute "as" in this case results in an XSLT processor validating the result of this template similarly).
3) Using the XPath SequenceType "as" attribute with such template rules is helpful, since an XSLT processor can do necessary validation of XML content produced by the template rule.
The following XSLT stylesheet transformation example [XSL3], illustrates use of XSLT template rules
that have parameter definition(s) on them (ref, the XSLT xsl:param
instruction).
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/info">
<result>
<xsl:variable name="c1" select="10" as="xs:integer"/>
<xsl:apply-templates select="val">
<xsl:with-param name="c1Val" select="$c1" as="xs:integer"/>
</xsl:apply-templates>
</result>
</xsl:template>
<xsl:template match="val" as="element(val)">
<xsl:param name="c1Val" as="xs:integer"/>
<val>
<xsl:attribute name="id" select="@id + $c1Val"/>
<xsl:value-of select="."/>
</val>
</xsl:template>
</xsl:stylesheet>
When an XML instance document [XML2] (provided earlier within this article) is transformed by the stylesheet [XSL3] as mentioned above, the following XSLT transformation result is produced.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<val id="11">hi</val>
<val id="12">hello</val>
<val id="13">there</val>
<val id="14">that's</val>
<val id="15">nice</val>
</result>
Following are the key design concepts with respect to the XSLT transformation example [XSL3] as illustrated above.
1) The XSLT transformation stylesheet [XSL3]
makes use of the usual XSLT xsl:template
(representing an XSLT template
rule) and xsl:apply-templates
instructions. An xsl:template
rule is invoked by xsl:apply-templates
instruction, for each of the relevant XDM nodes that are
available to the xsl:apply-templates
instruction.
2) The xsl:apply-templates
instruction via xsl:with-param
element(s) passes argument(s) to the template rule. The template
rule makes use of the passed argument(s) via xsl:param
element(s) (these
template rule parameters can be accessed within the template rule's body just
like XSLT variable references) along with the information about the XDM node (an
XDM node is an XPath runtime XML node information, constructed either from an
XML input document or from a literal XML fragment information available within
an XSLT stylesheet) that has been provided to the template rule via XSLT
template rule's "match" attribute.
3) The XSLT transformation stylesheet [XSL3],
also makes use of the XSLT 3.0 language's strong type checking via XPath 3.1's
data model type system based on XML Schema. The XSLT "as" attributes on various
XSLT stylesheet elements within this example, like "as" attribute on stylesheet
elements xsl:variable
, xsl:with-param
, xsl:param
and xsl:template
, ensures strong
type checking of content (via the XPath sequence type values like xs:integer,
element(val) etc) referred by these stylesheet elements. Making an XSLT
transformer perform such type checking brings the general benefit of
programming language strong typing within XSLT stylesheets.
2.1.2 XSLT named templates
We shall discuss within this section the XSLT named templates which are another kind of XSLT callable components.
Similar to XSLT template rules, an "XSLT named template" contains a sequence of XSLT language instructions enclosed within stylesheet tags <xsl:template> and </xsl:template>. The XSLT named templates are identified by the value of their "name" attribute (contrary to an XSLT stylesheet template rule's "match" attribute).
Within an XSLT stylesheet we cannot have more than one named template having the same value of their "name" attribute.
Following is an XSLT stylesheet example [XSL4] that uses a named template, and solves the same XSLT transformation use case as the XSLT document [XSL3] illustrated earlier within this article.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/info">
<result>
<xsl:variable name="c1" select="10" as="xs:integer"/>
<xsl:for-each select="val">
<xsl:call-template name="processValElem">
<xsl:with-param name="valElem" select="." as="element(val)"/>
<xsl:with-param name="c1Val" select="$c1" as="xs:integer"/>
</xsl:call-template>
</xsl:for-each>
</result>
</xsl:template>
<xsl:template name="processValElem" as="element(val)">
<xsl:param name="valElem" as="element(val)"/>
<xsl:param name="c1Val" as="xs:integer"/>
<val>
<xsl:attribute name="id" select="$valElem/@id + $c1Val"/>
<xsl:value-of select="$valElem"/>
</val>
</xsl:template>
</xsl:stylesheet>
When an XML instance document [XML2] (provided earlier within this article), is transformed by the stylesheet [XSL4] as mentioned above, the following XSLT transformation result is produced,
<?xml version="1.0" encoding="UTF-8"?>
<result>
<val id="11">hi</val>
<val id="12">hello</val>
<val id="13">there</val>
<val id="14">that's</val>
<val id="15">nice</val>
</result>
Following are the key design concepts with respect to the XSLT transformation example [XSL4] as illustrated above.
1) An initial template rule (having xsl:template
attribute specification
match="/info") starts the stylesheet XML document transformation
processing. Within this stylesheet template rule, an xsl:for-each
loop iterates
for each XML input document sibling element "val". Within each xsl:for-each
loop
iteration, an XSLT named template "processValElem" is invoked via an XSLT
instruction xsl:call-template
. With a named template call we pass two argument
values via xsl:with-param
elements.
An XSLT named template uses the XML element node structure of an element "val" (available as value of one of the named template's argument) and other argument details, to produce an XSLT transformation result for a particular XML element "val".
2) This XSLT stylesheet transformation does relevant type checking at various stylesheet transformation locations via an XSL SequenceType attribute "as". Following is a summary of type checking performed within this stylesheet using an XSL SequenceType attribute "as":
2.1) The type checking expression as="xs:integer", ensures that only an integer value is allowed to be assigned to a stylesheet variable, the template call argument, or as a value to be assigned to a template's parameter.
2.2) The type checking expression as="element(val)" ensures that only an XML element node named "val" is allowed to be assigned to a stylesheet's template call argument, a value to be assigned to a template's parameter, and that the result type of XSL template's transformation result to conform to this provided SequenceType.
2.1.3 XSLT stylesheet functions
We shall discuss within this section the XSLT stylesheet functions (we shall refer these with the phrase "stylesheet functions" at various places within this article) which are another kind of stylesheet callable components, and are one of the mechanisms to define user defined functions to be able to be called from within XPath expressions. These features make stylesheet functions very useful to be available within the XSLT language, since by using stylesheet functions we're able to extend the collection of functions to be able to be called from within XPath expressions in almost an unlimited way.
The XSLT stylesheet functions were first introduced within XSLT 2.0 language specification. They are also available within
XSLT 3.0 language as well. The XSLT stylesheet functions, are defined using
an XML stylesheet element xsl:function
.
Let's study and analyze an XSLT stylesheet transformation example below within this section, illustrating use of stylesheet functions.
Following is an XML input document [XML3], that we'll transform with an XSLT stylesheet further below within this section.
<?xml version="1.0" encoding="UTF-8"?>
<info>
<val1>10</val1>
<val2>2</val2>
</info>
Following is an XSLT stylesheet [XSL5], that we'll use to transform the XML document [XML3] mentioned above.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn1="http://fn1"
exclude-result-prefixes="xs fn1"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/info">
<result>
<res1>
<xsl:value-of select="fn1:binaryOp(function($a as xs:integer, $b as xs:integer) as xs:integer { $a + $b }, val1, val2)"/>
</res1>
<res2>
<xsl:value-of select="fn1:binaryOp(function($a as xs:integer, $b as xs:integer) as xs:integer { $a - $b }, val1, val2)"/>
</res2>
<res3>
<xsl:value-of select="fn1:binaryOp(function($a as xs:integer, $b as xs:integer) as xs:integer { $a * $b }, val1, val2)"/>
</res3>
</result>
</xsl:template>
<!-- A stylesheet function (that works as a generic logical binary operator implemented by this stylesheet function), that has
three parameters. The first parameter is a function item (that accepts two integer arguments, and returns an integer result).
The other two parameters of this stylesheet function are integer parameters, that're provided as arguments to the function
item parameter.
-->
<xsl:function name="fn1:binaryOp" as="xs:integer">
<xsl:param name="f1" as="function(xs:integer, xs:integer) as xs:integer"/>
<xsl:param name="a" as="xs:integer"/>
<xsl:param name="b" as="xs:integer"/>
<xsl:sequence select="$f1($a, $b)"/>
</xsl:function>
</xsl:stylesheet>
When an XML input document [XML3] is transformed with the stylesheet [XSL5], the following XML document output result is produced.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<res1>12</res1>
<res2>8</res2>
<res3>20</res3>
</result>
Following are the key design concepts with respect to the XSLT transformation example [XSL5] as illustrated above.
1) Within the XSLT stylesheet document of this
example we've defined a stylesheet function named "fn1:binaryOp
" that may be
called from within XPath expressions within this stylesheet. Please note that,
the XSLT stylesheet functions can only be called from within XPath
expressions (from the point of view of, having XPath expressions being able to
call stylesheet functions is conceptually same as calling XPath built-in
functions from within XPath expressions). As required by XSLT specification, the name of stylesheet
functions need to have a non null namespace as part of stylesheet function's
name (for this purpose within the stylesheet for this example, we've
declared the XML namespace xmlns:fn1="http://fn1"
and
have used the XML namespace prefix "fn1" as part of stylesheet function's name
[i.e "fn1:binaryOp
" as for this stylesheet transformation example]).
2) The algorithm of the stylesheet function
"fn1:binaryOp
" as for this example, is explained within XML comments provided
with definition of the stylesheet function "fn1:binaryOp
". Essentially, this
stylesheet function requires following three arguments from a function call: a
function item argument and two integer arguments. The function body of this xsl:function
stylesheet function, uses the function item to call the function
item's function and passes the locally available integer arguments to this
function item. Please note that a function item is a compiled representation of
XPath inline function expressions (for e.g, function($a as xs:integer, $b as
xs:integer) as xs:integer { $a + $b }
as provided within this stylesheet, and
also two other such XPath inline function expressions available within this
stylesheet).
An XSLT stylesheet function doesn't need to be as complex as illustrated within the stylesheet transformation example [XSL5] above. Passing an XPath inline function expressions as argument(s) to a stylesheet function makes such stylesheet functions serve advanced stylesheet function use cases but the stylesheet functions designed in this way could be useful for various XSLT transformation use cases.
Let's study and analyze an example of simple use of a stylesheet function, as illustrated within the following XSLT stylesheet [XSL6].
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn1="http://fn1"
exclude-result-prefixes="xs fn1"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="degreeCelsiusTemps" select="(5, 10.2, 15, 20.7, 25)" as="xs:double*"/>
<xsl:template match="/">
<result>
<xsl:for-each select="$degreeCelsiusTemps">
<xsl:variable name="degreesTemp" select="."/>
<temperature degrees="{$degreesTemp}">
<xsl:value-of select="fn1:degreeCelsiusToFahrenheit($degreesTemp)"/>
</temperature>
</xsl:for-each>
</result>
</xsl:template>
<!-- A stylesheet function, that converts a degrees Celsius temperature value
to Fahrenheit temperature value, and rounds the result to two decimal
places.
-->
<xsl:function name="fn1:degreeCelsiusToFahrenheit" as="xs:double">
<xsl:param name="c1" as="xs:double"/>
<xsl:sequence select="round(($c1 * (9 div 5)) + 32, 2)"/>
</xsl:function>
</xsl:stylesheet>
Following is a use case description and key design concepts with respect to the XSLT transformation example [XSL6] as illustrated above.
An XSLT stylesheet function "fn1:degreeCelsiusToFahrenheit" provided within this stylesheet example converts a xs:double typed
degrees Celsius temperature value, to a xs:double typed Fahrenheit temperature
value. The stylesheet example [XSL6] has a variable
"degreeCelsiusTemps" which refers to a sequence of xs:double values
(representing few sample degrees Celsius temperature values) which are iterated
via a xsl:for-each
loop, and each of these degrees Celsius value is converted to a
Fahrenheit value via call to the stylesheet function "fn1:degreeCelsiusToFahrenheit".
The XSLT stylesheet [XSL6] illustrated above produces the following stylesheet transformation XML document output,
<?xml version="1.0" encoding="UTF-8"?>
<result>
<temperature degrees="5">41</temperature>
<temperature degrees="10.2">50.36</temperature>
<temperature degrees="15">59</temperature>
<temperature degrees="20.7">69.26</temperature>
<temperature degrees="25">77</temperature>
</result>
Within the above mentioned stylesheet transformation XML document output, an XML document fragment like <temperature degrees="10.2">50.36</temperature> has following semantic meaning,
The value of an XML attribute temperature/@degrees (i.e, 10.2) refers to a temperature degrees celsius input value, and an XML element <temperature>'s child text node's value (i.e, 50.36) refers to the corresponding temperature's Fahrenheit value.
2.2 XPath callable components
The following subsections describe in some detail about the various kinds of XPath callable components.
2.2.1 XPath built-in library functions
There are various XPath built-in library functions available in 3.1 version of XPath language. Their complete list and definitions are available within the following W3C specification link: https://www.w3.org/TR/xpath-functions-31/.
The XPath built-in functions are simplest forms of XPath language callable components, since the XPath built-in functions are called from within XPath expressions to produce a particular result at the place where they're called.
2.2.2 XPath user defined functions
An XPath 3.1 language, specifies a particular syntax (referred to as XPath "inline function expression") to define user defined functions that may be called from within XPath expressions. These XPath inline function expressions are anonymous functions (i.e, these functions don't have function name). When an XPath inline function expression is compiled by an XPath processor, a corresponding XPath Data Model (XDM) function item is produced referring to which an XPath user defined function may be called.
The XPath user defined functions (i.e, function items) may also be passed as
XSLT template (template rule and named template) parameter arguments, or as function arguments (both as arguments to
XPath functions, and XSLT stylesheet functions defined via xsl:function
element).
The ability to define XPath user defined functions, and to use within XPath expressions and XSLT stylesheets, is a very useful functional capability provided by XPath and XSLT languages. By defining XPath user defined functions, we could extend the collection of XPath functions to be able to be used within XPath expressions and XSLT stylesheets, in almost unlimited ways, thereby supporting software developers to solve complex XSLT and XPath use cases.
An XPath inline function expression defining an XPath user defined function has following syntactic structure : zero or more function parameters, a function body (with possible references to function parameters), and an optional result value type definition.
Within an XSLT stylesheet, the XPath user defined
functions are usually referenced as values of XSLT variables (i.e, via an XSLT
element xsl:variable
).
Let's study and analyze an example of simple use of an XPath user defined function, as illustrated within the following XSLT stylesheet [XSL7].
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="func1" select="function ($a as xs:integer, $b as xs:integer) as xs:integer { ($a * $b) + 5 }"/>
<xsl:template match="/">
<result>
<one>
<xsl:value-of select="$func1(1, 2)"/>
</one>
<two>
<xsl:value-of select="$func1(2, 3)"/>
</two>
<three>
<xsl:value-of select="$func1(3, 4)"/>
</three>
</result>
</xsl:template>
</xsl:stylesheet>
An XSLT stylesheet [XSL7] , illustrated above, produces the following stylesheet transformation XML document output.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<one>7</one>
<two>11</two>
<three>17</three>
</result>
Following are the key design concepts, with respect to the XSLT transformation example [XSL7] as illustrated above.
1) This stylesheet specifies an XPath inline function expression as a value of an XSLT variable named "func1". This XPath inline function expression, is a definition of a function with following syntactic structure: The function accepts two integer arguments, evaluates the body of the function, and produces an integer valued result (which is the result of evaluation of function's body).
2) The XPath user defined function referred as value of an XSLT variable "func1", is called from three locations within an XSLT stylesheet (using the XPath dynamic function call syntax like $func1(1, 2)). Each of these XPath dynamic function calls, passes different argument values during the function invocation.
Let's now further study and analyze within this section below, an XSLT transformation example of using an XPath user defined function that constructs and returns XML complex content (an XML complex content, has at least one XML element with optional attribute(s) on any of the elements).
Following is an XML input document [XML4] that we'll transform with an XSLT stylesheet further below within this section.
<?xml version="1.0" encoding="UTF-8"?>
<info>
<val priority="2" a="1" b="2"/>
<val priority="2" a="2" b="3"/>
<val priority="1" a="3" b="4"/>
<val priority="2" a="4" b="5"/>
<val priority="3" a="1" b="2"/>
<val priority="3" a="2" b="3"/>
<val priority="3" a="3" b="4"/>
<val priority="1" a="5" b="6"/>
<val priority="1" a="6" b="7"/>
<val priority="1" a="7" b="8"/>
<val priority="2" a="8" b="9"/>
</info>
Following is an XSLT stylesheet [XSL8] that we'll use to transform the XML document [XML4] mentioned above.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn0="http://fn0"
exclude-result-prefixes="xs fn0"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<!-- A variable referring to an, XPath inline function expression -->
<xsl:variable name="constructGrpResultWithDelims" select="function($prefixDelimElemName as xs:string,
$suffixDelimElemName as xs:string,
$valElems as element(val)*) as element()*
{ (fn0:constructDelimElem($prefixDelimElemName),
fn0:groupValElemsByPriority($valElems),
fn0:constructDelimElem($suffixDelimElemName))
}"/>
<xsl:template match="/info">
<result>
<xsl:copy-of select="$constructGrpResultWithDelims('groupsStart', 'groupsEnd', val)"/>
</result>
</xsl:template>
<!-- A stylesheet function, that constructs an XML element given an element's name. -->
<xsl:function name="fn0:constructDelimElem" as="element()">
<xsl:param name="delimElemName" as="xs:string"/>
<xsl:element name="{$delimElemName}"/>
</xsl:function>
<!-- A stylesheet function, that groups (via xsl:for-each-group instruction) a sequence of
XML 'val' elements by the specified grouping criteria (via xsl:for-each-group
instruction's "group-by" attribute).
-->
<xsl:function name="fn0:groupValElemsByPriority" as="element(valElems)*">
<xsl:param name="valElems" as="element(val)*"/>
<xsl:for-each-group select="$valElems" group-by="xs:integer(@priority)">
<valElems priority="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<val a="{@a}" b="{@b}"/>
</xsl:for-each>
</valElems>
</xsl:for-each-group>
</xsl:function>
</xsl:stylesheet>
When an XML input document [XML4] is transformed with the stylesheet [XSL8], the following XML document output result is produced.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<groupsStart/>
<valElems priority="2">
<val a="1" b="2"/>
<val a="2" b="3"/>
<val a="4" b="5"/>
<val a="8" b="9"/>
</valElems>
<valElems priority="1">
<val a="3" b="4"/>
<val a="5" b="6"/>
<val a="6" b="7"/>
<val a="7" b="8"/>
</valElems>
<valElems priority="3">
<val a="1" b="2"/>
<val a="2" b="3"/>
<val a="3" b="4"/>
</valElems>
<groupsEnd/>
</result>
Following are the key design concepts, with respect to the XSLT transformation example [XSL8] as illustrated above.
1) This stylesheet example illustrates implementing an XPath user defined function (an XPath inline function expression for this function is assigned to an XSLT variable "constructGrpResultWithDelims"), that can produce an XML complex content via call to a stylesheet function (named "fn0:groupValElemsByPriority" in this example). Within this XSLT stylesheet example, the stylesheet function "fn0:groupValElemsByPriority" does grouping of XML "val" input elements, on the basis of integer value of "val" element's attribute @priority.
2) An XPath user defined function (which is assigned to XSLT variable "constructGrpResultWithDelims") has a function body which constructs an XDM sequence by concatenating results of three stylesheet function calls. As we can see, the function body of the user defined function (which is assigned to XSLT variable "constructGrpResultWithDelims") is following : (fn0:constructDelimElem($prefixDelimElemName), fn0:groupValElemsByPriority($valElems), fn0:constructDelimElem($suffixDelimElemName)). This XPath user defined function's body follows the XPath sequence constructor syntax using comma operator as defined by XPath 3.1 specification [ a simple representative example of this is : (1, 2, 3, 4, 5) ]. The function call fn0:constructDelimElem($prefixDelimElemName) that is invoked twice within this XPath sequence constructor expression adds prefix and suffix elements (represented by the following XML serialization output : <groupsStart/>, <groupsEnd/>) as siblings to the result of stylesheet function call fn0:groupValElemsByPriority($valElems).
We should note that, although as within an XSLT stylesheet example [XSL8] illustrated within this section above, we've assigned an XPath inline function expression to an XSLT variable "constructGrpResultWithDelims" and we call this function via an XPath dynamic function call expression $constructGrpResultWithDelims('groupsStart', 'groupsEnd', val), an XPath inline function expression always represents an anonymous function (i.e, an XPath function that doesn't have a name at an XPath language level).
Interestingly also we could discuss why the XSLT transformation grouping output of stylesheet [XSL8] (as illustrated above) shows the XML elements with the following result order:
<valElems priority="2">
<valElems priority="1">
<valElems priority="3">
Since, for the XSLT stylesheet [XSL8] we did the XML data grouping with an attribute @priority (and being aware of the data type xs:integer for an attribute @priority), the user might have wanted to have the XML grouping result data reflecting the following order of XML data groups.
<valElems priority="1">
<valElems priority="2">
<valElems priority="3">
This may be achieved by modifying the use of xsl:for-each-group
instruction as follows (i.e, using an XSLT instruction
xsl:sort
to sort the XML data groups).
<xsl:for-each-group select="$valElems"
group-by="xs:integer(@priority)">
<xsl:sort select="current-grouping-key()"/>
......
The above mentioned change to stylesheet [XSL8] shall produce the following XSLT transformation result.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<groupsStart/>
<valElems priority="1">
<val b="4" a="3"/>
<val b="6" a="5"/>
<val b="7" a="6"/>
<val b="8" a="7"/>
</valElems>
<valElems priority="2">
<val b="2" a="1"/>
<val b="3" a="2"/>
<val b="5" a="4"/>
<val b="9" a="8"/>
</valElems>
<valElems priority="3">
<val b="2" a="1"/>
<val b="3" a="2"/>
<val b="4" a="3"/>
</valElems>
<groupsEnd/>
</result>
3) XPath 3.1 Higher Order Functions
Now that we have discussed in detail within the previous sections of this article what we mean by XSLT and XPath callable components, we'll study and analyze within this section what are the concepts related to XPath 3.1 higher order functions. The XPath 3.1 higher order functions are one of the kinds of XPath language callable components.
The XPath 3.1 F&O specification defines higher order functions as following.
The higher order functions are, functions that accept functions among their arguments, or that return functions in their result.
There are following two ways to use XPath higher order functions within XPath expressions and/or XSLT stylesheets.
3.1) Using the XPath 3.1 built-in higher order functions
The XPath 3.1 F&O specification defines a few specific XPath higher order functions ready to be used within XPath expressions and/or XSLT stylesheets. These are defined at the following XPath 3.1 F&O specification link: https://www.w3.org/TR/xpath-functions-31/#basic-hofs.
Let's study and analyze for the purpose of this article two of the XPath 3.1 built-in higher order functions in the following paragraphs.
a) The fn:filter
function
The fn:filter
function has following function signature,
and a brief definition thereafter as defined by the XPath 3.1 F&O specification,
fn:filter($seq as item()*, $f as function(item()) as
xs:boolean) as item()*
The fn:filter
function returns those items from the
sequence $seq for which the supplied function $f returns true.
Following is an XML input document [XML5] that we'll transform with an XSLT stylesheet further below within this section.
For the XSLT stylesheet transformation example [XSL8], the reason why the XSLT instruction xsl:for-each-group when used without the xsl:sort instruction produced the result the way it did is following.
The XSLT instruction xsl:for-each-group
when used without the xsl:sort
instruction, produces XML data
groups output in an XML data order known as "order of first appearence" as
defined by XSLT 3.0 language. With this way of producing XML data groups result,
the XML data groups output order is defined entirely by the XML data nodes orders
within an XML input document (i.e, the XML document order. With this XSLT's
default xsl:for-each-group
result data order algorithm: the XML data group that
appears first within the stylesheet transformation output is the one whose
first XML output node appears first within an XML input document).
By using an XSLT xsl:sort
instruction as a child
instruction of xsl:for-each-group
, we're able to reorder the XML data groups within the
stylesheet transformation output (with this, the entire XML data groups are
reordered in the output, and nothing within any of the group's output changes).
<?xml version="1.0" encoding="UTF-8"?>
<info>
<val>1.1</val>
<val>2.1</val>
<val>3.1</val>
<val>4.1</val>
<val>5.1</val>
<val>6.1</val>
<val>7.1</val>
<val>8.1</val>
</info>
Following is an XSLT stylesheet [XSL9] that we'll use to transform the XML document [XML5] mentioned above.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/info">
<result>
<xsl:copy-of select="filter(val, function($elem) { xs:double($elem/text()) lt 6 })"/>
</result>
</xsl:template>
</xsl:stylesheet>
When an XML input document [XML5] is transformed with the stylesheet [XSL9], the following XML document output result is produced.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<val>1.1</val>
<val>2.1</val>
<val>3.1</val>
<val>4.1</val>
<val>5.1</val>
</result>
Following are the key design concepts with respect to the XSLT transformation example [XSL9] as illustrated above.
The XSLT stylesheet invokes the fn:filter
function with
following two arguments: a sequence of XML "val" elements, and an XPath
function item. The fn:filter
function returns those XML "val" elements, whose
content's xs:double numeric value satisfies a particular boolean condition (for
this example, an XML "val" element content's xs:double numeric value needs to be
less than 6, for that XML element to be selected by the fn:filter
function).
b) The fn:for-each-pair
function
The fn:for-each-pair
function has following function signature,
and a brief definition thereafter as defined by the XPath 3.1 F&O specification.
fn:for-each-pair($seq1 as item()*,
$seq2 as item()*,
$action as function(item(), item()) as item()*) as item()*
The fn:for-each-pair
function, applies the function item
$action to successive pairs of items taken one from $seq1 and one from $seq2,
returning the concatenation of the resulting sequences in order.
Following is an XML input document [XML6], that we'll transform with an XSLT stylesheet further below within this section.
<?xml version="1.0" encoding="UTF-8"?>
<info>
<val a="1" b="2"/>
<val a="2" b="3"/>
<val a="3" b="4"/>
<val a="4" b="5"/>
<val a="5" b="6"/>
<val a="6" b="7"/>
<val a="7" b="8"/>
<val a="8" b="9"/>
</info>
Following is an XSLT stylesheet [XSL10], that we'll use to transform the XML document [XML6] mentioned above.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/info">
<result>
<xsl:for-each select="for-each-pair(val/@a, val/@b, function($a, $b) { xs:double($a) + xs:double($b) })">
<res>
<xsl:value-of select="."/>
</res>
</xsl:for-each>
</result>
</xsl:template>
</xsl:stylesheet>
When an XML input document [XML6] is transformed with the stylesheet [XSL10], the following XML document output result is produced.
<?xml version="1.0" encoding="UTF-8"?>
<result>
<res>3</res>
<res>5</res>
<res>7</res>
<res>9</res>
<res>11</res>
<res>13</res>
<res>15</res>
<res>17</res>
</result>
Following are the key design concepts, with respect to the XSLT transformation example [XSL10] as illustrated above,
1) The fn:for-each-pair
function's first argument is a
sequence of XML attributes val/@a, the second argument is a sequence of XML
attributes val/@b, and the third argument is a "function item" that adds two
xs:double numeric values.
2) The fn:for-each-pair
function's "function item" third
argument adds function item's two xs:double arguments, and appends that
summation result to the final result sequence of the fn:for-each-pair
function.
3.2) Defining and using XPath 3.1 custom higher order functions
Within the previous section of this article, we've studied and analyzed two of XPath 3.1 language's built-in higher order functions.
We can also define and use our own, custom XPath higher order functions for specific needs of XPath expression and XSLT stylesheet authors, using the syntactic facilities of XPath 3.1 language's inline function expressions.
Following is an XSLT stylesheet [XSL11] example, that illustrates defining and using custom XPath 3.1 higher order functions.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="binaryOp" select="function($f1 as function(*),
$a as xs:double,
$b as xs:double) as xs:double { $f1($a, $b) }"/>
<xsl:variable name="add" select="function($a as xs:double, $b as xs:double) as xs:double { $a + $b }"/>
<xsl:variable name="subtract" select="function($a as xs:double, $b as xs:double) as xs:double { $a - $b }"/>
<xsl:variable name="multiply" select="function($a as xs:double, $b as xs:double) as xs:double { $a * $b }"/>
<xsl:variable name="divide" select="function($a as xs:double, $b as xs:double) as xs:double { $a div $b }"/>
<xsl:template match="/">
<result>
<one>
<xsl:value-of select="$binaryOp($add, 7, 2)"/>
</one>
<two>
<xsl:value-of select="$binaryOp($subtract, 7, 2)"/>
</two>
<three>
<xsl:value-of select="$binaryOp($multiply, 7, 2)"/>
</three>
<four>
<xsl:value-of select="$binaryOp($divide, 7, 2)"/>
</four>
</result>
</xsl:template>
</xsl:stylesheet>
When the XSLT stylesheet [XSL11] illustrated above is run as a stylesheet transformation, the following stylesheet transformation result is produced.
<?xml version="1.0"
encoding="UTF-8"?>
<result>
<one>9</one>
<two>5</two>
<three>14</three>
<four>3.5</four>
</result>
Following are the key design concepts, with respect to the XSLT transformation example [XSL11] as illustrated above:
1) An XPath inline function expression, that is assigned to an XSLT variable "binaryOp", is a user defined higher order function (since this XPath function, accepts an XPath function item as an argument). The XPath sequence type expression function(*) specified with the first parameter of this function definition means that, this higher order function can accept any function item as an argument.
2) We've further specified within this stylesheet, four more function definitions via XPath inline function expressions (referred to by XSLT variables "add", "subtract", "multiply", "divide") that are passed as arguments during different calls to the function referred by XSLT variable "binaryOp" (which is an XPath higher order function, due to these mentioned characteristics).
4) Summary
At the beginning of this article, we've mentioned within an introduction the definition and uses of XSLT and XPath languages, along with brief definition and description of XPath 3.1 higher order functions (which are commonly abbreviated as "HOF"). Then in the next section, we've discussed in detail about XSLT and XPath language callable components. The XPath 3.1 language higher order functions, are one of the kinds of XSL languages callable component.
The XSLT stylesheet transformation examples (along with XPath expressions used within those stylesheets) illustrated within this article, might seem to be basic in term of their use case utility. The XSLT stylesheet transformation examples illustrated within this article, have been written to illustrate the syntax and semantics of various XSLT and XPath language constructs. For the real world XML document transformation use cases, the XSLT stylesheets for them should likely have much more complex codebases and their integrations with one or more other software application tools and technologies.
All the XSLT and XPath examples provided within this article, have been tested with Apache XalanJ XSLT 3.0 processor (currently available as a prototype implementation, supporting fairly large number of XSLT 3.0 and XPath 3.1 language features), and Saxon-HE (Home Edition) v 12.3 XSLT 3.0 processor.
Thanks to the readers of this perhaps lengthy article, for spending their valuable time reading this article.
5) References
The Extensible Stylesheet Language Family (XSL) home page:
* https://www.w3.org/Style/XSL/
Related W3C specifications:
* XSL Transformations (XSLT) Version 3.0
* XML Path Language (XPath) 3.1
* XPath and XQuery Functions and Operators 3.1
* W3C XML Schema Definition Language (XSD) 1.1 Part 1: Structures
* W3C XML Schema Definition Language (XSD) 1.1 Part 2: Datatypes
XSLT and XPath processors:
* Saxon processor for XSLT, XQuery, and XML Schema
Related Wikipedia article: