Tuesday, September 12, 2023
XSLT 3.0, XPath 3.1 and XalanJ
Monday, April 10, 2023
XPath 2.0 quantified expressions. Implementation with XSLT 1.0
XPath 2.0 language has introduced new syntax and semantics as compared to XPath 1.0 language, for e.g like the XPath 2.0 quantified expressions.
Following is an XPath 2.0 grammar, for the quantified expressions (quoted from the XPath 2.0 language specification),
QuantifiedExpr ::= ("some" | "every") "$" VarName "in" ExprSingle ("," "$" VarName "in" ExprSingle)* "satisfies" ExprSingle
The XPath 2.0 quantified expression, when evaluated over a list of XPath data model items, returns either boolean 'true' or a 'false' value.
I'm able to, suggest an XSLT 1.0 code pattern (tested with Apache XalanJ), that can implement the logic of XPath 2.0 like quantified expressions. Following is an example, illustrating these concepts,
XML input document:
<?xml version="1.0" encoding="UTF-8"?>
<elem>
<a>5</a>
<a>5</a>
<a>4</a>
<a>7</a>
<a>5</a>
<a>5</a>
<a>7</a>
<a>5</a>
</elem>
XSLT 1.0 stylesheet, implementing the XPath 2.0 "every" like quantified expression (i.e, universal quantification):
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
exclude-result-prefixes="exslt"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/elem">
<xsl:variable name="temp">
<xsl:for-each select="a">
<xsl:if test="number(.) > 3">
<yes/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="count(exslt:node-set($temp)/yes) = count(a)"/>
</xsl:template>
</xsl:stylesheet>
The above XSLT stylehseet, produces a boolean 'true' result, if all XML "a" input elements have value greater than 3, otherwise a boolean 'false' result is produced.
XSLT 1.0 stylesheet, implementing the XPath 2.0 "some" like quantified expression (i.e, existential quantification):
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
exclude-result-prefixes="exslt"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/elem">
<xsl:variable name="temp">
<xsl:for-each select="a">
<xsl:if test="number(.) = 4">
<yes/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="count(exslt:node-set($temp)/yes) >= 1"/>
</xsl:template>
</xsl:stylesheet>
The above XSLT stylehseet, produces a boolean 'true' result, if at-least one XML "a" input element has value equal to 4, otherwise a boolean 'false' result is produced.
Within the above cited XSLT 1.0 stylesheets, we've used XSLT "node-set" extension function (that helps to convert an XSLT 1.0 "result tree fragment" into a node set).
We can therefore conclude that, within an XSLT 1.0 environment, we can largely simulate logic of many XPath 2.0 language constructs.
Thursday, April 6, 2023
XSLT 1.0 transformation : find distinct values
In continuation to my previous blog post on this site, this blog post describes how to use XSLT 1.0 language (tested with Apache XalanJ 2.7.3 along with its JavaScript extension function bindings), to find distinct values (i.e, doing de-duplication of data set) from data set originating from an XML instance document.
Following is an XSLT transformation example, illustrating these features.
XML instance document:
<?xml version="1.0" encoding="UTF-8"?>
<elem>
<a>2</a>
<a>3</a>
<a>3</a>
<a>5</a>
<a>3</a>
<a>1</a>
<a>2</a>
<a>5</a>
</elem>
Corresponding XSLT 1.0 transformation:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
xmlns:js="http://js_functions"
extension-element-prefixes="js"
version="1.0">
<xsl:output method="text"/>
<xalan:component prefix="js" functions="reformString">
<xalan:script lang="javascript">
function reformString(str)
{
return str.substr(0, str.length - 1);
}
</xalan:script>
</xalan:component>
<xsl:template match="/elem">
<xsl:if test="count(a) > 0">
<xsl:variable name="result">
<xsl:call-template name="distinctValues">
<xsl:with-param name="curr_node" select="a[1]"/>
<xsl:with-param name="csv_result" select="concat(string(a[1]), ',')"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="js:reformString(string($result))"/>
</xsl:if>
</xsl:template>
<xsl:template name="distinctValues">
<xsl:param name="curr_node"/>
<xsl:param name="csv_result"/>
<xsl:choose>
<xsl:when test="$curr_node/following-sibling::*">
<xsl:variable name="temp1">
<xsl:choose>
<xsl:when test="not(contains($csv_result, concat(string($curr_node), ',')))">
<xsl:value-of select="concat($csv_result, string($curr_node), ',')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$csv_result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="distinctValues">
<xsl:with-param name="curr_node" select="$curr_node/following-sibling::*[1]"/>
<xsl:with-param name="csv_result" select="normalize-space($temp1)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$csv_result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The above mentioned, XSLT transformation produces the following, desired result,
2,3,5,1
XalanJ users could find the, JavaScript language related jars (which needs to be available within, the jvm classpath at run-time during XSLT transformation) within XalanJ src distribution. These relevant jar files are : bsf.jar, commons-logging-1.2.jar, rhino-1.7.14.jar (Rhino is mozilla's javascript engine implementation, bundled with XalanJ 2.7.3 src distribution).
Wednesday, April 5, 2023
XSLT 1.0 transformation : finding maximum from a list of numbers, from an XML input document
Apache Xalan project has released XalanJ 2.7.3 few days ago, and I thought to write couple of blog posts here, to report on the basic sanity of XalanJ 2.7.3's functional quality.
Following is a simple XML transformation requirement.
XML input document :
<?xml version="1.0" encoding="UTF-8"?>
<elem>
<a>2</a>
<a>3</a>
<a>5</a>
<a>1</a>
<a>7</a>
<a>4</a>
</elem>
We need to write an XSLT 1.0 stylesheet, that outputs the maximum value from the list of XML "a" elements mentioned within above cited XML document.
Following are the three XSLT 1.0 stylesheets that I've come up with, that do this correctly,
1)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/elem">
<xsl:variable name="temp">
<xsl:for-each select="a">
<xsl:sort select="." data-type="number" order="descending"/>
<e1><xsl:value-of select="."/></e1>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="concat('Maximum : ', exslt:node-set($temp)/e1[1])"/>
</xsl:template>
</xsl:stylesheet>
2)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/elem">
Maximum : <xsl:call-template name="findMax"/>
</xsl:template>
<xsl:template name="findMax">
<xsl:variable name="temp">
<xsl:for-each select="a">
<xsl:sort select="." data-type="number" order="descending"/>
<e1><xsl:value-of select="."/></e1>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="exslt:node-set($temp)/e1[1]"/>
</xsl:template>
</xsl:stylesheet>
3)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/elem">
<xsl:choose>
<xsl:when test="count(a) = 0"/>
<xsl:when test="count(a) = 1">
Maximum : <xsl:value-of select="a[1]"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="result">
<xsl:call-template name="findMax">
<xsl:with-param name="curr_max" select="a[1]"/>
<xsl:with-param name="next_node" select="a[2]"/>
</xsl:call-template>
</xsl:variable>
Maximum : <xsl:value-of select="$result"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="findMax">
<xsl:param name="curr_max"/>
<xsl:param name="next_node"/>
<xsl:choose>
<xsl:when test="$next_node/following-sibling::*">
<xsl:choose>
<xsl:when test="number($next_node) > number($curr_max)">
<xsl:call-template name="findMax">
<xsl:with-param name="curr_max" select="$next_node"/>
<xsl:with-param name="next_node" select="$next_node/following-sibling::*[1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="findMax">
<xsl:with-param name="curr_max" select="$curr_max"/>
<xsl:with-param name="next_node" select="$next_node/following-sibling::*[1]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="number($next_node) > number($curr_max)">
<xsl:value-of select="$next_node"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$curr_max"/>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I somehow, personally like the XSLT solution 3) illustrated above, for these requirements. This solution, traverses the sequence of XML "a" elements till the end of "a" elements list, and outputs the maximum value from the list at the end of XML elements traversal. This solution, seems to have an algorithmic time complexity of O(n), with a little bit of possible overhead of XSLT recursive template calls than the other two XSLT solutions.
The XSLT solutions 1) and 2) illustrated above, seem to have higher algorithmic time complexity than solution 3), due to the use of XSLT xsl:sort instruction (which probably has algorithmic time complexity of O(n * log(n)) or O(n * n)). The XSLT solutions 1) and 2) illustrated above, also seem to have higher algorithmic "space complexity" (this measures the memory used by the algorithm) due to storage of intermediate sorted result.
The XalanJ command line, to run above cited XSLT transformations are following,
java org.apache.xalan.xslt.Process -in file.xml -xsl file.xsl
Wednesday, March 29, 2023
A simple XSLT stylesheet, XML document validator
I've been thinking that, this shall be interesting to share.
Please consider following, XSLT 1.0 document transformation definition.
XML input document:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a>2</a>
<a>4</a>
<a>6</a>
<a>8</a>
<a>10</a>
</root>
We should be able to tell, that this XML document is valid, if all XML /root/a elements within it have even numbers.
The following XSLT 1.0 stylesheet just does this XML document validation check,
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exslt="http://exslt.org/common"
exclude-result-prefixes="exslt"
version="1.0">
<!-- An XSLT stylesheet, that checks whether values of all XML
input /root/a elements have even numbers (in which case, the XML input
document is reported as valid). -->
<xsl:output method="text"/>
<xsl:template match="/root">
<xsl:variable name="result">
<xsl:for-each select="a">
<e1><xsl:value-of select=". mod 2"/></e1>
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="count(exslt:node-set($result)/*[. = 0]) = count(exslt:node-set($result)/*)">
<xsl:text>XML document is valid</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>XML document is in-valid</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Please note that, within above mentioned XSLT 1.0 stylesheet, we've used an XSLT 1.0 extension function "node-set", that is supported by most of the XSLT 1.0 engines (for example, XalanJ as described here https://xalan.apache.org/xalan-j/apidocs/org/apache/xalan/lib/ExsltCommon.html).
For the interest of readers, following is an equivalent XML Schema 1.1 validation, that solves the same problem,
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element name="a" type="xs:integer" maxOccurs="unbounded"/>
</xs:sequence>
<xs:assert test="count(a) = count(a[. mod 2 = 0])"/>
</xs:complexType>
</xs:element>
</xs:schema>
Personally, speaking, I shall prefer an XML Schema 1.1 validation for this requirement, since XML Schema language is designed to do XML document validation, whereas XSLT language is designed to do an XML document transformation (but as illustrated within this blog post, the XSLT stylesheet does the job of an XML document validator as well).
Wednesday, September 21, 2022
XPath/XSLT 1.0 data model and beyond
Is the inherent XPath/XSLT 1.0 data model better from the point of view of functional capabilities, or the data models of next versions (2.0, 3.0) of these language specifications?
XPath/XSLT 1.0 data model, focuses on having a well-formed XML document tree as part of the data model. Whereas, 2.0 and 3.0 versions of these language specifications, focus on having a flat sequence of data model items (like atomic/list values or XML nodes). Many of the XPath/XSLT 2.0 and 3.0 use cases, still focus on achieving well-formed XML document trees as part of the output of an XSLT transform.
Although, the definition of data models for 1.0 versions of these language specifications, is fundamentally different than 2.0 versions of these language specifications (one is a coherent XML tree, whereas the newer version is a sequence of data model items), the XSLT 1.0 and 2.0/3.0 transforms try to achieve the same end-result (i.e, an XML well-formed serialization of the data model instance).
I think, XSLT 2.0/3.0 brought sequence of data model items as a fundamental new definition of data model, because XPath 2.0/3.0 data model components need to be strongly typed at a granular level (aligning with XML Schema specification).
If we need, greater strongly typed process of achieving an end-result of the XSLT transform, we should select the 2.0/3.0 versions of these language specifications. Otherwise we should opt for the 1.0 versions of these specifications.
The 2.0/3.0 versions of these language specifications, have brought in newer XSLT language features, and also a vastly expanded function library. That's an advantage of using the XSLT 2.0/3.0 languages, than the 1.0 version of these languages.
At various times, I'm not desirous of too much strong typing (in an XML Schema sense) within an XSLT transformation process (because that involves, greater design effort upfront), and if my XML transformation requirements are simple I tend to opt for an XSLT 1.0 transform. I certainly go for, XSLT 2.0/3.0 options, if I'm not constrained by these factors.
Monday, May 30, 2022
XML Schema : identity constraints essentials and best practices
In this blog post, I'll attempt to describe the best practices, for the use of XML Schema (XSD) identity constraints. I'm going to compare here, the XSD identity constraint instructions xs:unique and xs:key, and describe when to use which one of these.
The XSD xs:key serves the same purpose within XML, as the RDBMS primary keys, whereas XSD xs:unique is a generic syntax to enforce unique values within a set of XML data values. xs:key also enforces, unique values within a set of XML data values. Unlike xs:key, xs:unique permits the values within a XML dataset to be absent (i.e, logically speaking as null values).
Please consider following XML Schema validation example.
XML Schema document:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="catalog" type="CatalogType">
<xs:unique name="prodNumKey">
<xs:selector xpath="*/product"/>
<xs:field xpath="number"/>
</xs:unique>
</xs:element>
<xs:complexType name="CatalogType">
<xs:sequence>
<xs:element name="department" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="product" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="number" type="xs:positiveInteger" minOccurs="0"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="price">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:decimal">
<xs:attribute name="currency" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="number" type="xs:positiveInteger"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
One of valid XML instance document, for the above mentioned XSD schema, is following:
<catalog>
<department number="021">
<product>
<number>557</number>
<name>Short-Sleeved Linen Blouse</name>
<price currency="USD">29.99</price>
</product>
<product>
<name>Ten-Gallon Hat</name>
<price currency="USD">69.99</price>
</product>
<product>
<number>443</number>
<name>Deluxe Golf Umbrella</name>
<price currency="USD">49.99</price>
</product>
</department>
</catalog>
Pleas note, the following, within above cited XML Schema validation example,
1) Within the XML Schema document, the "number" child of "product" is specified as following,
<xs:element name="number" type="xs:positiveInteger" minOccurs="0"/>
(i.e, with minOccurs="0", meaning that this element is optional within the corresponding XML instance document)
2) The "catalog" element has following XSD xs:unique definition bound to it,
<xs:unique name="prodNumKey">
<xs:selector xpath="*/product"/>
<xs:field xpath="number"/>
</xs:unique>
The above stated facts, mean that, the "number" element is not intended to function as the primary key of "product" data set (because, the primary key value has to be present within all the records of the data set), but for the set of "number" elements that are present within the mentioned XML instance document (the "number" element can be absent within certain "product" elements, as per the above mentioned XML Schema document) their values have to be unique.
We've discussed, the role of XSD xs:unique instruction within above mentioned paragraphs.
Now, as we've stated earlier within this blog post, how do we enforce primary key kind of behavior within an XML Schema document.
Within the context, of above mentioned example, this can be simply done by changing the "number" element declaration to following (i.e, we must not write minOccurs="0" within the XML element declaration),
<xs:element name="number" type="xs:positiveInteger"/>
And, write the "catalog" element declaration as following (i.e, we now use xs:key instead of xs:unique),
<xs:element name="catalog" type="CatalogType">
<xs:key name="prodNumKey">
<xs:selector xpath="*/product"/>
<xs:field xpath="number"/>
</xs:key>
</xs:element>
The above changes to the XML Schema document mean that,
All "product" elements must have a "number" child, and all the "number" values within XML instance document have to be unique (and, these characteristics shall make, "number" element as a primary key for "product" data set).
The XML Schema features, related to constructs xs:unique and xs:key, described within this blog post, are supported both by 1.0 and 1.1 versions of XML Schema language.
Acknowledgements : The XML Schema validation example, mentioned within this blog post is borrowed from Priscilla Walmsley's excellent book "Definitive XML Schema, 2nd edition".