Showing posts with label xslt. Show all posts
Showing posts with label xslt. Show all posts

Sunday, December 31, 2023

XSLT 3.0 grouping use case

I've just been playing this evening, trying to improve XalanJ prototype processor's XSLT 3.0 xsl:for-each-group instruction's implementation. Following is an xsl:for-each-group instruction use case, that I've been trying to solve.

XML input document,

<?xml version="1.0" encoding="utf-8"?>

<root>

  <a>

    <itm1>hi</itm1>

    <itm2>hello</itm2>

    <itm3>there</itm3>

  </a>

  <b>

    <itm1>this</itm1>

    <itm2>is</itm2>

    <itm3>nice</itm3>

  </b>

  <c>

    <itm1>hello</itm1>

    <itm2>friends</itm2>

  </c>

  <d>

    <itm1>this is ok</itm1>

  </d>

</root>

XSLT 3.0 stylesheet, using xsl:for-each-group instruction to group XML instance elements from an XML document cited above,

<?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="/root">

           <result>

               <xsl:for-each-group select="*" group-by="(count(*) eq 1) or (count(*) eq 3)">

            <group groupingCriteria="{if (current-grouping-key() eq true()) then '1,3' else 'not(1,3)'}">

                <xsl:copy-of select="current-group()"/>

            </group>

              </xsl:for-each-group>

          </result>

      </xsl:template>

</xsl:stylesheet>

The stylesheet transformation result, of above cited XSLT transform is following as produced by XalanJ,

<?xml version="1.0" encoding="UTF-8"?><result>

  <group groupingCriteria="1,3">

    <a>

    <itm1>hi</itm1>

    <itm2>hello</itm2>

    <itm3>there</itm3>

  </a>

    <b>

    <itm1>this</itm1>

    <itm2>is</itm2>

    <itm3>nice</itm3>

  </b>

    <d>

    <itm1>this is ok</itm1>

  </d>

  </group>

  <group groupingCriteria="not(1,3)">

    <c>

    <itm1>hello</itm1>

    <itm2>friends</itm2>

  </c>

  </group>

</result>

Achieving such XML data grouping, was very hard with XSLT 1.0 language. Thank god, we've XSLT 3.0 language available now.


Thursday, December 28, 2023

Managing complexity of XPath 3.1 'if' expressions, in the context of XSLT 3.0

I've just been playing around, with the following XSLT transformation example, and thought of sharing this as a blog post here.

Let's consider following XSLT 3.0 stylesheet, that we'll use to transform an XML document mentioned thereafter,

<?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"/>

  <xsl:variable name="date1" select="xs:date('2005-10-12')" as="xs:date"/>

  <xsl:template match="/root">

      <root>

          <xsl:copy-of select="if (fn0:func1($date1)) then a else b"/>

     </root>

  </xsl:template>

  <!-- An XSLT stylesheet function, that performs a specific boolean valued computation. The result of this function, is used to perform computations of distinct branches of XPath 'if' condition used within xsl:copy-of instruction written earlier above. -->

 <xsl:function name="fn0:func1" as="xs:boolean">

     <xsl:param name="date1" as="xs:date"/>

     <xsl:sequence select="if (current-date() lt $date1) 

                                                                               then true() 

                                                                               else false()"/>

   </xsl:function>

</xsl:stylesheet>

The corresponding XML instance document is following,

<?xml version="1.0" encoding="utf-8"?>

<root>

    <a/>

    <b/>

</root>

The two possible XSLT transformation results (depending upon the result of following XPath expression comparison : current-date() lt $date1, for the above mentioned XSLT transformation are following:

<?xml version="1.0" encoding="UTF-8"?><root>

  <b/>

</root>

and,

<?xml version="1.0" encoding="UTF-8"?><root>

  <a/>

</root>

Within the above mentioned XSLT transformation example, we may observe how, the XPath 3.1 'if' expressions have been written to achieve the desired XSLT transformation results. We're able to write stylesheet functions that may be significantly complex to produce boolean result, which may act as XPath 'if' expression branching condition.

I hope that, the above mentioned XSLT transformation example is useful.


Wednesday, December 27, 2023

XML data grouping with XSLT 3.0, illustrations

I've just been playing this morning, writing an XSLT 3.0 stylesheet, that does grouping of an XML input data as follows (that I wish to share with XML and XSLT community).

XML input document,

<root>

  <a>

    <m/>

  </a>

  <b>

    <n/>

  </b>

  <a>

    <o/>

  </a>

  <a>

    <p/>

  </a>

  <a>

    <q/>

  </a>

  <b>

    <r/>

  </b>

  <b>

    <s/>

  </b>

</root>


XSLT 3.0 stylesheet, that does grouping of XML document's data mentioned above (i.e, grouping of xml element children of element "root"),

<?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="/root">

     <xsl:for-each-group select="*" group-by="name()">

        <xsl:element name="{current-grouping-key()}">

           <xsl:copy-of select="current-group()/*"/>

        </xsl:element>

     </xsl:for-each-group>

  </xsl:template>

</xsl:stylesheet>


The XSLT transformation output, of this XML document transform is following,

<?xml version="1.0" encoding="UTF-8"?><a>

  <m/>

  <o/>

  <p/>

  <q/>

</a><b>

  <n/>

  <r/>

  <s/>

</b>


The XML data grouping algorithm implemented by the XSLT stylesheet illustrated above is following,

The XML element children of element "root", are formed into multiple groups (there are two XML data groups that're possible for this stylesheet transformation example.) on the basis of XML element names (the XML sibling elements which are child elements of element "root").

I hope that, this XSLT stylesheet example has been useful for us to study.

This XSLT stylesheet example, has been tested with Apache XalanJ's XSLT 3.0 prototype processor.

Tuesday, September 12, 2023

XSLT 3.0, XPath 3.1 and XalanJ

It's been a while that, I've written a blog post here. I've few new updates, about the work which XalanJ team has been doing over the past few months, that I wish to share with the XML community.

XalanJ project, provides XSLT and XPath processors that are written with Java language. An XSLT processor transforms an XML input document (or even only text files), into other formats like XML, HTML and text.

XalanJ project, has released a new version (2.7.3) of XalanJ on 2023-04-01. This XalanJ release, essentially is a bug fix release over the previous release. The XalanJ 2.7.3 release was extensively tested by XalanJ team, and it has very good compliance with XSLT 1.0 and XPath 1.0 specs.

Since Apr 2023, XalanJ team has been working to develop implementations of XSLT 3.0 and XPath 3.1 language specifications. These XalanJ codebase changes are currently not released by XalanJ team, but are available on XalanJ dev repos branch.

I further wish to write about, XSLT 3.0 user-defined callable component implementation enhancements within XalanJ, that should be available within one of the future XalanJ release. The callable components within a programming language are, essentially functions and procedures. XSLT 1.0 language has only one kind of user-defined callable component, which is written with an XML element name xsl:template.

XSLT 3.0 provides another kind of user-defined callable component, defined with an XML element name xsl:function. An XSLT instruction xsl:function was first made available within XSLT 2.0 language. A user-defined function present within an XSLT stylesheet, may be called within an XPath expression.

Following is an example of XSLT 3.0 stylesheet, that makes use of an xsl:function element,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                         xmlns:ns0="http://ns0"
                         exclude-result-prefixes="ns0"
                         version="3.0">
    
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="/">       
         <result>
             <one>
                 <xsl:value-of select="ns0:func1(6, 5, true(), false())"/>
             </one>
             <two>
         <xsl:value-of select="ns0:func1(2, 5, true(), false())"/>
             </two>
         </result>
    </xsl:template>
    
    <xsl:function name="ns0:func1">
         <xsl:param name="val1"/>
         <xsl:param name="val2"/>
         <xsl:param name="a"/>
         <xsl:param name="b"/>
       
         <xsl:value-of select="if ($val1 gt $val2) then ($a and $b) else ($a or $b)"/>
    </xsl:function>
    
</xsl:stylesheet>

The above cited XSLT stylesheet, defines an user-defined function named "func1" bound to the specified non-null XML namespace. This function definition requires four arguments with a function call, and produces a boolean result based on few logical conditions.

The above cited XSLT stylesheet, produces following output with XalanJ,

<?xml version="1.0" encoding="UTF-8"?><result>
  <one>false</one>
  <two>true</two>
</result>

XPath 3.1 provides a new kind of callable component (that wasn't available with XPath 1.0), which is an inline function definition which when compiled by an XPath processor, produces an XPath data model (XDM) function item.

An XPath 3.1 function item, may be called via an XPath dynamic function call expression.

Following is an XSLT 3.0 stylesheet, that specifies an XPath inline function expression, and is an alternate solution to above cited XSLT stylesheet,

<?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:variable name="func1" select="function($val1, $val2, $a, $b) { if ($val1 gt $val2) then ($a and $b) else ($a or $b) }"/>
    
    <xsl:template match="/">       
         <result>
             <one>
                   <xsl:value-of select="$func1(6, 5, true(), false())"/>
             </one>
             <two>
          <xsl:value-of select="$func1(2, 5, true(), false())"/>
             </two>
         </result>
    </xsl:template>
    
</xsl:stylesheet>

The above cited XSLT stylesheet, specifies an XPath inline function expression assigned to an XSLT variable "func1". This makes, XPath expressions like $func1(..) as function calls (which are termed as dynamic function calls by XPath 3.1 language).

The above cited XSLT stylesheet, produces an output with XalanJ, which is same as with an earlier cited stylesheet.

Its perhaps also interesting to discuss and analyze, which of the above mentioned XSLT callable components approaches an XSLT stylesheet author should choose?

An XPath 3.1 inline function expression is an *XPath expression*, therefore its function body is limited to have XPath syntax only.

Whereas, an xsl:function is an XSLT instruction (which may be invoked as a function call, from within XPath expressions). The xsl:function function's body may have significantly complex logic (with any permissible XSLT syntax and XPath expressions) as compared to XPath inline function expressions.

To conclude, I believe that, when using XSLT 3.0 and XPath 3.1, we have following three main kinds of user-defined callable components which may be used by XSLT stylesheet authors,

1) xsl:template   (which is very important within an XSLT stylesheet, and is the core of an XSLT stylesheet)

2) xsl:function

3) XPath inline function expression

That's all I wished to say within this blog post.



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(.) &gt; 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) &gt;= 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) &gt; 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.jarcommons-logging-1.2.jarrhino-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) &gt; 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) &gt; 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.

Friday, March 29, 2019

XSLT 1.0 transformations for large xml input documents

I thought that, this topic could be of interest to XML community.

I've discovered an interesting aspect of JAXP API (Java API for XML Processing) that seems to have relations to streaming that we talk with XSLT 3.0. Please see following document, and an example given in its section 4.12 (that explains JAXP's StAX API and using it with JAXP's transformation APIs)


Using the cited JAXP code in above document, one can transform very large XML input documents (I've tried an XML input document with size of about 700 MB, that worked) using XSLT 1.0 (the JDK's built in JAXP implementation can do this. I've tried with JDK 1.8 which works fine for this). It can do certain kinds of XSLT 1.0 transformations with very large XML input documents, very well. When doing the same transformations with XSLT 2.0, or with XSLT 3.0 in non streaming way, we would usually get following errors 'java.lang.OutOfMemoryError: Java heap space'.

I've written few complete examples for this topic here, https://github.com/mukulga/largexml_xslt10.

Notes: My intention for writing this blog post is not to endorse in any way that XSLT 1.0 is better than XSLT 2.0/3.0 for every aspect. XSLT 2.0/3.0 have various new language features as compared to XSLT 1.0, that raise the productivity of XSLT developers and allow development of XSLT stylesheets with ease that could be much more complex in terms of functionality, than with XSLT 1.0.

Tuesday, June 13, 2017

XSLT 3.0 reaches W3C Recommendation status

Not long ago, XSLT 3.0 has reached the W3C Recommendation status. Below is the link to the XSLT 3.0 spec:

https://www.w3.org/TR/2017/REC-xslt-30-20170608/

XSLT 3.0 is a very advanced and useful language, as compared to XSLT 2.0. One of the main features (among others) introduced in XSLT 3.0, is that the XSLT transformation can be done in streaming mode.

Sunday, November 1, 2009

XSLT 1.0: Regular expression string tokenization, and Xalan-J

Some time ago, XSLT folks were debating on xsl-list (ref, http://www.biglist.com/lists/lists.mulberrytech.com/xsl-list/archives/200910/msg00365.html) about how to implement string tokenizer functionality in XSLT. XPath 2.0 (and therefore, XSLT 2.0) has a built in function for this need (ref, fn:tokenize). XPath 2.0 string tokenizer method, 'fn:tokenize' takes a string and a tokenizing regular expression pattern as arguments. This is something, which cannot be done natively in XSLT 1.0. To do this, with XSLT 1.0 we need to write a recursive tokenizing "named XSLT template". But a "named XSLT template" using XSLT 1.0, for string tokenization has limitation, that it cannot accept natively an arbitrary regular expression, as a tokenizing delimiter.

I got motivated enough, to write a Java extension mechanism for regular expression based, string tokenization facility for XSLT 1.0 stylesheets, using the Xalan-J XSLT 1.0 engine.

Here's Java code and a sample XSLT stylesheet for this particular, functionality:

String tokenizer Xalan-J Java extension:
package org.apache.xalan.xslt.ext;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.xpath.NodeSet;
import org.w3c.dom.Document;

public class XalanUtil {
    public static NodeSet tokenize(String str, String regExp) throws ParserConfigurationException {
      String[] tokens = str.split(regExp);
      NodeSet nodeSet = new NodeSet();
       
      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
      DocumentBuilder docBuilder = dbf.newDocumentBuilder();
      Document document = docBuilder.newDocument();
       
      for (int nodeCount = 0; nodeCount < tokens.length; nodeCount++) {
        nodeSet.addElement(document.createTextNode(tokens[nodeCount]));   
      }
       
      return nodeSet;
    }
}
Sample XSLT stylesheet, using the above Java extension (named, test.xsl):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                version="1.0"                                                    
                xmlns:java="http://xml.apache.org/xalan/java"
                exclude-result-prefixes="java">
                 
   <xsl:output method="xml" indent="yes" />
   
   <xsl:param name="str" />
   
   <xsl:template match="/">
     <words>
       <xsl:for-each select="java:org.apache.xalan.xslt.ext.XalanUtil.tokenize($str, '\s+')">
         <word>
           <xsl:value-of select="." />
         </word>
       </xsl:for-each>
     </words>
   </xsl:template>
   
 </xsl:stylesheet>
Now for e.g, when the above stylesheet is run with Xalan as follows: java -classpath <path to the extension java class> org.apache.xalan.xslt.Process -in test.xsl -xsl test.xsl -PARAM str "hello world", following output is produced:
<?xml version="1.0" encoding="UTF-8"?>
<words>
 <word>hello</word>
 <word>world</word>
</words>

This illustrates, that regular expression based string tokenization was applied as designed above, for XSLT 1.0 environment.

The above Java extension, should be running fine with a min JRE level of, 1.4 as it relies on the JDK method, java.lang.String.split(String regex) which is available since JDK 1.4.

PS: For easy reading and verboseness, the package name in the above Java extension class may be omitted, which will cause the corresponding XSLT instruction to be written like following:
xsl:for-each select="java:XalanUtil.tokenize(... I would personally prefer this coding style, for production Java XSLT extensions. Though, this should not matter and to my opinion, decision to handle this can be left to individual XSLT developers.

I hope, that this was useful.

Monday, June 15, 2009

Running first XSLT 2.0 stylesheet with IBM XSLT 2.0 engine

I could run my first XSLT 2.0 stylesheet with IBM XSLT 2.0 engine (ref, WAS XML Feature Pack Open Beta).

I tried the following XSLT 2.0 stylesheet, using xsl:for-each-group instruction, which worked well with the IBM XSLT engine.


<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">

<xsl:output method="xml" indent="yes" />

<xsl:template match="books">
<books>
<xsl:for-each-group select="book" group-by="author">
<author name="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<book>
<xsl:copy-of select="name" />
<xsl:copy-of select="publisher" />
</book>
</xsl:for-each>
</author>
</xsl:for-each-group>
</books>
</xsl:template>

</xsl:stylesheet>

Thursday, April 23, 2009

WAS XML Feature Pack Open Beta

There was an annoucement recently from IBM (http://webspherecommunity.blogspot.com/2009/04/was-open-xml-feature-pack-beta.html), about availability of "WAS XML Feature Pack Open Beta" supporting XPath 2.0, XSLT 2.0 and XQuery 1.0. It was good to know this.

Therefore, users would be able to use XPath 2.0, XSLT 2.0 and XQuery 1.0 in a WAS environment, using IBM's own processors for these languages.

This is an early preview release, with more enhancements expecting to come later.

I'm looking forward to try these language processors myself.

Friday, January 16, 2009

Normalizing unnecessary whitespace text nodes during XSLT transformation

Let's say that my input XML is following,

<test>
<a/>
<b/>
<c/>
<d/>
<e/>
<f>some data ..</f>
</test>

I need to write an XSLT transformation, which just removes elements, 'c' and 'd' and keeps rest of the structure same.

The result of the transformation should be following [1]:

<test>
<a/>
<b/>
<e/>
<f>some data ..</f>
</test>

The obvious solution to this problem is, to write a modified identity transformation logic.

i.e.,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="xml" indent="yes" />

<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>

<xsl:template match="c | d" />

</xsl:stylesheet>

But there is a subtle flaw in this logic. The actual output produced by the above stylesheet is,

<?xml version="1.0" encoding="UTF-8"?>
<test>
<a/>
<b/>


<e/>
<f>some data ..</f>
</test>

There are a kind of two whitepace holes in the output (created by the elements which are removed). This makes the output not 100% same as the desired output [1].

The whitespace holes in the output above can be very well explained. They are actually the newline whitespaces (near the elements 'c' and 'd') present in the original document, which are preserved in the generated output.

Adding a little bit of extra logic in the stylesheet can fix this problem.

The right solution will be following,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:output method="xml" indent="yes" />

<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>

<xsl:template match="c | d" />

<xsl:template match="text()[(normalize-space() = '') and (preceding-sibling::node()[1]/self::c or preceding-sibling::node()[1]/self::d)]" />

</xsl:stylesheet>


Please note the last template in this stylesheet, which fixed the whitespaces problem for me.

Saturday, January 10, 2009

XSLT functions returning void

I just thought, whether XSLT 2.0 functions can return something like a void value.

for e.g. as we could do,

public void myfunction() {

}

in Java.

I do not think there is any syntax in XSLT which allows this declaration.

i.e., can we do something like following in XSLT.

<xsl:function name="my:testfunction" as="a-void-type">
<!-- something here -->
</xsl:function>


I think there is no way of specifying a void type in XSLT. A function must return something. The best we could do is, that we specify the return type as, as="xs:string?". i.e., the function may return a xs:string value, or it may return nothing (i.e., an empty sequence: ()).

But if we want to return something like void from a function, we can instead implement a named template for this. i.e., something like following,


<xsl:template name="testtemplate">
<!-- something here -->
</xsl:template>


The named template is conceptually similar to xsl:function (both are callable modules), but there are some subtle differences as well, between them. xsl:function is a lot more loosely coupled module than the named template. The named template inherits the context from the caller, whereas the function has no access to the context information of the caller (though any piece of the context can be passed to the function as parameters).

Sunday, January 4, 2009

XSLT: sorting data by duration

I've this input text file (test.txt):

A started at 03:12:10
A ended at 03:20:20
B started at 03:20:25
B ended at 03:22:21
C started at 03:22:23
C ended at 03:22:55
D started at 03:22:57
D ended at 03:23:21
E started at 03:23:25
E ended at 03:24:40

Here A, B, C etc. are some events, and they start at a particular time and end at another time.

I need to produce an output like following:

D : 0-0-24
C : 0-0-32
E : 0-1-15
B : 0-1-56
A : 0-8-10

i.e., events sorted by the time they took (in ascending order of durations). The duration format in the output is, hr-min-sec.

The following XSLT 2.0 stylesheet worked well for this problem,
<?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="2.0">

<xsl:output method="text" />

<xsl:variable name="time-data" select="tokenize(unparsed-text('test.txt', 'UTF-8'), '\r?\n')" />

<xsl:template match="/">
  <xsl:variable name="temp-data">
    <xsl:for-each select="$time-data">
      <xsl:variable name="val" select="normalize-space(.)" />
      <xsl:variable name="pos" select="position()" />
      <xsl:if test="position() mod 2 = 1">
        <data key="{tokenize($val, '\s')[1]}">
          <xsl:value-of select="xs:time(tokenize($time-data[$pos + 1], '\s')[last()]) -xs:time(tokenize($val, '\s')[last()])" />
        </data>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>
  <xsl:for-each select="$temp-data/*">
    <xsl:sort select="xs:dayTimeDuration(.)" />
    <xsl:variable name="hr" select="hours-from-duration(.)" />
    <xsl:variable name="min" select="minutes-from-duration(.)" />
    <xsl:variable name="sec" select="seconds-from-duration(.)" />
    <xsl:value-of select="@key" /> : <xsl:value-of select="concat($hr, '-', $min, '-', $sec)" /> <xsl:text>
</xsl:text>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

The following XSLT sort instruction worked, well for this use case:
<xsl:sort select="xs:dayTimeDuration(.)" order="descending" />

I've used Saxon to solve this.

If anybody bumps by this post, and could think of a better solution (particularly written in a more functional style, and avoiding the temporary tree), I would be very glad to know that.

Sunday, December 28, 2008

A static code quality tool, for XSLT code

I wrote a little tool to measure and enhance the code quality, of XSLT programs/scripts. It's available at, http://gandhimukul.tripod.com/xslt/xslquality.html.

I hope that the XSLT community might find this useful.

Any comments/suggestions about this tool would be most welcome.

Saturday, November 22, 2008

Are multiple XPath predicates same as boolean "and" operator

I had a doubt about this concept, and asked following question on xsl-list.

Supposing I write the following XPath expressions,

1) X[c1][c2] or specifying generically, X[c1][c2][]...[cn]

2) X[c1 and c2] or specifying generically, X[c1 and c2 ... and cn]

where c1, c2 etc. are boolean expressions.

are the two forms (1 & 2) above exactly equivalent (i.e., will they return the same nodeset/sequence)? I think yes ... but just wanted to confirm with the list.

if 1 & 2 are exactly equivalent, then what could be the rule of thumb for using which form in certain scenarios?

There was a good discussion on the list about this, and list members shared some useful thoughts.

Below is a summary of the points we discussed on the list.

1. David Carlisle

> are the two forms (1 & 2) above exactly equivalent

No

compare

X[position()=2][position()=2]

and

[position()=2 and position()=2]

the first one is

()

the second is

X[2]

David further wrote,

context position (position()) and size (last()) do change. so basically repeated filters are equivalent to and unless any of them depend on position() or last(), including the special case of [integer] being equivalent to [position()=integer]
this last case is what makes it tricky to do a static rewrite of this.

If you have

X[... foo ..][... bar ...]

you can only rewrite that to

X[(... foo ..) and (... bar ...)]

if you know that neither expression will evaluate to a number at run time.


2. Vasu Chakkera

If that were true, then the condition

myelement[@myattribute][1] should be same as
myelement[1][@myattribute], which is not true...

The predicate order is important

in a typical "and"

[a and b] = [b and a]


3. Andrew Welch

Only / will change the context node, so I would've thought one predicate after the other is pretty much equivalent apart from cases that rely on size of the selection (which is the only thing that changes after each predicate).

Mukul: I asked a related question in continuation to this.

for real world XSLT/XPath programs, upto how many predicates can we typically see?

I haven't seen programs using 3, 4 or more predicates.

X[..][..][..][..]

I have used only one or two predicates upto now.

are excessively large number or predicates really useful? (though, the syntax allows that)

I think perhaps, for complex 'and' conditions, using multiple predicates are useful.

David shared an interesting observation about this:

He has been using some stylesheets having upto 11 predicates.

He wrote:

> are excessively large number or predicates really useful? (though, the syntax allows that)

isn't that like asking if complicated expressions are useful? they are useful if you need them, otherwise they are not.

3 or 4 predicates is totally routine but the most common reason for having larger numbers is to filter attributes

[not(@purpose='iemode')]
[not(@purpose='artifact')]
[not(@purpose='w-dimension')]

is equivalent to

[not(@purpose='iemode') and
[not(@purpose='artifact') and
[not(@purpose='w-dimension')]

but I'd almost always use the first form in XSLT 1.0 because it's easier to indent and easier to refactor, but if starting from the beginning in XSLT 2.0 I'd write it as

[not(@purpose=('iemode','artifact','w-dimension'))]

Mukul: This was a nice discussion I believe, and I have learnt few useful concepts.

Tuesday, November 4, 2008

fn:contains -> multiple strings to compare with

An XSLT user asked following question on xsl-list:

I want to have something that does this: contains('$d/ris:organ/text()', 'Hamburg' or 'Koblenz' or 'xxx'...) ===> Compare 1 String with multpile strings.

instead of: contains('$d/ris:organ/text()','Hamburg') or contains('$d/ris:organ/text()','Koblenz')...

Andrew Welch suggested following answer:

some $x in ('Hamburg', 'Koblenz', 'xxx') satisfies
contains($d/ris:organ/text(), $x)


This uses the XPath 2.0 quantified expression, "some".

This is cool.

I was prompted to share Andrew's answer here, because I thought of a lengthy and perhaps inefficient solution for this (I feel a bit stupid, actually :) ):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="http://my-functions"
version="2.0">

<xsl:output indent="yes" omit-xml-declaration="yes" />

<xsl:template match="/">
<xsl:variable name="str" select="'hello xxx dd'" />
<xsl:variable name="list" select="('Hamburg','Koblenz','xxx')" />

<xsl:if test="my:contains($str, $list)">
matches
</xsl:if>

</xsl:template>

<!-- a custom 'contains' implementation -->
<xsl:function name="my:contains" as="xs:boolean">
<xsl:param name="str" as="xs:string" />
<xsl:param name="list" as="xs:string+" />

<xsl:variable name="temp" as="xs:boolean*">
<xsl:for-each select="$list">
<xsl:if test="contains($str, .)">
<xsl:sequence select="xs:boolean('true')" />
</xsl:if>
</xsl:for-each>
</xsl:variable>

<xsl:sequence select="if ($temp[1] = xs:boolean('true')) then
xs:boolean('true') else xs:boolean('false')" />

</xsl:function>

</xsl:stylesheet>