XSLT

XSLT reference

Useful identity templates

String funciton string-length()

Output xml entities as is

<xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>

Handling CDATA XSL can't copy CDATA section as CDATA section. http://sources.redhat.com/ml/xsl-list/2001-05/msg00980.html

XPath's axes http://www.w3schools.com/XPath/xpath_axes.asp

Useful XPath samples

<!-- Return the value of the attribute @name where the text of the textarea element is equal to apostrophe -->
<xsl:value-of select="//textarea[text()='apostrophe']/@name"/>
 
<!-- Return the value of the attribute @name where the children elements of tr element satisfy the following conditions:
-the text of the td element is equal to Min AND 
-td has input child element which has a name attribute
-->
<xsl:value-of select="//tr/td[text()='Min']/../td/input/@name"/>
 
 
<!-- Go down to the path but select the parent node -->
<!-- Find span element where the text() is equals to 'Any Label' and then select its parent div node. -->
<xsl:value-of select="//span[contains(text(), 'Any Label')]/parent::div"/>
 
<!-- Select all nodes that contains @title attribute -->
<xsl:value-of select="//*[@title]"/>

Advanced used of XSLT: http://www2.informatik.hu-berlin.de/~obecker/XSLT/

Declarations in XML

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="file:///C:/path/example.xsl"?> <!-- XSL -->
 
<!-- XSD file is added on the root tag -->
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="file:///C:/path/example.xsd">

Custom Functions

Custom XSL functions

Change letters to upper or lower cases

<?xml version="1.0" encoding="UTF-8"?>
<!--
  Change letters to upper or lower cases
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8" />
 
  <xsl:template match="*">
    <xsl:variable name="myString">John Doe</xsl:variable>
 
    <!-- Define lower and upper case letters -->
    <xsl:variable name="LowerCaseLetters">abcdefghijklmnopqrstuvwxyz</xsl:variable>
    <xsl:variable name="UpperCaseLetters">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
 
    <!-- Convert lower cases to upper cases using translate function -->
    <xsl:value-of select="translate($myString, $LowerCaseLetters, $UpperCaseLetters)"/>
 
    <xsl:text>&#xa;</xsl:text><!-- Newline -->
 
    <!-- Convert upper cases to lower cases using translate function -->
    <xsl:value-of select="translate($myString, $UpperCaseLetters, $LowerCaseLetters)"/>
 
  </xsl:template>
 
</xsl:stylesheet>

Raise to power

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8" />
 
  <!-- Usage example: Calculate 10^5-->
  <xsl:template match="*">
    <xsl:param name="PowResult">
      <xsl:call-template name="Pow">
        <xsl:with-param name="Base" select="10"/>
        <xsl:with-param name="Exponent" select="5"/>
        <xsl:with-param name="Result" select="1"/><!-- Always set to 1 when Pow() function is used. -->
      </xsl:call-template>
    </xsl:param>
    <xsl:value-of select="$PowResult"/><!-- Display the result -->
  </xsl:template>
 
  <!-- Generic function: Pow($Base, $Exponent)-->
  <!-- Multiple $Base $Exponent time recursively until $Exponent is equal to 0. -->
  <xsl:template name="Pow">
    <xsl:param name="Base"/>
    <xsl:param name="Exponent"/>
    <xsl:param name="Result"/>
    <xsl:choose>
      <xsl:when test="$Exponent = 0">
        <xsl:value-of select="1"/>
      </xsl:when>
      <xsl:when test="$Exponent = 1">
        <xsl:value-of select="$Result * $Base"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="Pow">
          <xsl:with-param name="Base" select="$Base"/>
          <xsl:with-param name="Exponent" select="$Exponent - 1"/>
          <xsl:with-param name="Result" select="$Result * $Base"/>
        </xsl:call-template>                          
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
 
</xsl:stylesheet>

Removing leading zeros

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8" />
  <!-- 
    Usage example: Removing leading zeros 
  -->
  <xsl:template match="*">
    <xsl:param name="RemoveLeadingZerosResult">
      <xsl:call-template name="RemoveLeadingZeros">
        <xsl:with-param name="sTmp" select="00001"/>
      </xsl:call-template>   
    </xsl:param>
    <xsl:value-of select="$RemoveLeadingZerosResult"/><!-- Display the result -->
  </xsl:template>
 
  <!-- 
    Description: Generic function: RemoveLeadingZeros($sTmp).
      If the 1st character of the string is a "0", then remove the "0". 
      Repeat the same process recursively until the 1st character is not a "0". 
  -->
  <xsl:template name="RemoveLeadingZeros">
      <xsl:param name="sTmp"/>
      <xsl:choose>
        <xsl:when test="starts-with($sTmp, '0')">
          <xsl:choose>
            <xsl:when test="string-length($sTmp) = 1">
              <xsl:value-of select="$sTmp"/>
            </xsl:when>
 
            <xsl:otherwise>
              <xsl:call-template name="RemoveLeadingZeros">
                <xsl:with-param name="sTmp" select="substring($sTmp, 2, string-length($sTmp) - 1)"/>
              </xsl:call-template>      
            </xsl:otherwise>
          </xsl:choose>
        </xsl:when>
 
        <xsl:otherwise>
          <xsl:value-of select="$sTmp"/>
        </xsl:otherwise>
      </xsl:choose>
  </xsl:template>
 
</xsl:stylesheet>

How to handle apostrophe and quote in XSLT

The XML sample

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <textarea name="123'_a">apostrophe: 123'_a</textarea>
  <textarea name="123">b</textarea>
  <textarea name="123">a</textarea>
  <textarea name="123'_&quot;_a">apostrophe and quote: 123'_&quot;_a</textarea>
  <textarea name="123345">c</textarea>
  <textarea name="555">a</textarea>    
</root>

The XSL codes:

<?xml version="1.0" encoding="UTF-8"?>
<!--
Below are examples showing how to handle apostrophe and quote in XSLT
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8" />
 
  <xsl:template match="*">
 
    <!-- Examples of how to handle apostrophe and quote in XPath -->
    <!-- ******************************************************* -->
    <!-- Searching for 123'_a -->
    <xsl:for-each select="//textarea[@name=concat('123', &quot;'&quot;, '_a')]">
      <xsl:text>Found </xsl:text>
      <xsl:value-of select="."/>
      <xsl:text>&#xa;</xsl:text><xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
 
    <!-- Searching for 123'_"_a -->
    <xsl:for-each select="//textarea[@name=concat('123', &quot;'&quot;, '_', '&quot;', '_a')]">
      <xsl:text>Found </xsl:text>
      <xsl:value-of select="."/>
      <xsl:text>&#xa;</xsl:text><xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
 
    <!-- Examples of how to handle apostrophe and quote in XSL -->
    <!-- ***************************************************** -->
    <xsl:variable name="text">Translate():___'___'__"___"__&#xa;</xsl:variable>
 
    <!-- Embedded apostrophe in a variable and then use the variable -->
    <xsl:variable name="apos">'</xsl:variable>
    <xsl:value-of select="translate($text, $apos, 'V')"/>
 
    <xsl:if test="contains($text, $apos)">
      <xsl:text>Embedded apostrophe in a variable and then use it in contains()&#xa;&#xa;</xsl:text>
    </xsl:if>
 
    <!-- Use it between apostrophes -->
    <xsl:value-of select='translate($text, "&apos;", "A")'/>
 
    <xsl:if test='contains($text, "&apos;")'>
      <xsl:text>Use apostrophe between apostrophes: Used in contains()&#xa;&#xa;</xsl:text>
    </xsl:if>
 
    <!-- Use it between quotes -->
    <xsl:value-of select="translate($text, &quot;'&quot;, 'D')"/>
 
    <xsl:if test="contains($text, &quot;'&quot;)">
      <xsl:text>Use apostrophe between quotes: Used in contains()&#xa;&#xa;</xsl:text>
    </xsl:if>
  </xsl:template>
 
</xsl:stylesheet>

The output:

Found apostrophe: 123'_a
 
Found apostrophe and quote: 123'_"_a
 
Translate():___V___V__"___"__
Embedded apostrophe in a variable and then use it in contains()
 
Translate():___A___A__"___"__
Use apostrophe between apostrophes: Used in contains()
 
Translate():___D___D__"___"__
Use apostrophe between quotes: Used in contains()

References http://p2p.wrox.com/topic.asp?TOPIC_ID=52168 http://www.biglist.com/lists/lists.mulberrytech.com/xsl-list/archives/200312/msg00117.html http://kushalm.com/the-perils-of-xpath-expressions-specifically-escaping-quotes http://www.stylusstudio.com/xsllist/200104/post40540.html

Sample Template Code

Sample XSLT Template Code

Add / Change element name

Change element name

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
  Change element name:
    -While copying everything:
      -Catch the element you want to change.
      -Reconstruct the element you catched under another name.
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
 
    <!-- Copy everything -->
    <!-- ############# -->
    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
    </xsl:template>
 
    <xsl:template match="row"><!-- Catching element(eg. row) that you want to change -->
      <xsl:element name="Paragraph"><!-- Replace row element with Paragraph element by creating Paragraph element-->
        <xsl:apply-templates select="@*|node()"/><!-- Copy everything under row node and put them here -->
      </xsl:element>
    </xsl:template>
 
</xsl:stylesheet>



Add a new element when reference element is nth child of parent element

<?xml version="1.0" encoding="UTF-8"?>
<!-- Add new <entry> element if the <entry> is the 4th child of <row>.-->
<!-- ################## -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
 
  <!-- Copy everything -->
  <!-- ############# -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
 
  <!-- Catching <row> element -->
  <xsl:template match="row">
 
    <xsl:element name="row">
      <xsl:for-each select="entry">
        <!-- If <entry> is the 4th child of <row>, then add a new <entry> element.-->
        <xsl:if test="position()=4">
          <xsl:element name="entry"/>
        </xsl:if>
 
        <!-- Copy the 4th entry element -->
        <xsl:element name="entry">
          <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
 
  </xsl:template>
 
</xsl:stylesheet>

Copy XML file without comments

<?xml version="1.0" encoding="UTF-8"?>
<!-- 
Description: Copy content of XML file without comments.
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
 
  <!-- Copy everything -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
 
  <!-- Catch comments and do nothing. Hence, eliminating comments. -->
  <xsl:template match="comment()"/>
 
</xsl:stylesheet>

Indent content of XML file

<?xml version="1.0" encoding="UTF-8"?>
<!--
Description: Indent the content of the XML file.
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <xsl:output method="xml" encoding="UTF-8"/><!--Instruct how to output-->
 
  <xsl:param name="indent-increment" select="'   '"/>
  <xsl:template match="*">
    <xsl:param name="indent" select="'&#xA;'"/>
    <xsl:value-of select="$indent"/>
    <xsl:copy>
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates>
        <xsl:with-param name="indent" select="concat($indent, $indent-increment)"/>
      </xsl:apply-templates>
      <xsl:if test="*">
        <xsl:value-of select="$indent"/>
      </xsl:if>
    </xsl:copy>
  </xsl:template>
 
  <xsl:template match="comment()|processing-instruction()">
      <xsl:copy/>
  </xsl:template>
 
  <xsl:template match="text()[normalize-space(.)='']"/>
 
</xsl:stylesheet>

Loops

Decrement Looping

<?xml version="1.0" encoding="UTF-8"?>
<!-- Decrement Looping: Recursively decrementing specified value down to 1. -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="text" encoding="UTF-8"/>
 
    <xsl:template match="/">
        <!-- Calling DecrementLooping template -->
        <xsl:call-template name="DecrementLooping">
          <xsl:with-param name="i" select="9"/>
        </xsl:call-template>
     </xsl:template>
 
    <!-- Implementation of DecrementLooping template -->
    <!-- Recursively decrementing specified value down to 1. -->
    <xsl:template name="DecrementLooping">
      <xsl:param name="i" select="1"/>
 
      <xsl:if test="$i &gt; 0">
        <xsl:value-of select="$i"></xsl:value-of><xsl:text>&#xa;</xsl:text>
        <xsl:call-template name="DecrementLooping">
          <xsl:with-param name="i" select="$i - 1"/>
        </xsl:call-template>
      </xsl:if>
 
    </xsl:template>
 
</xsl:stylesheet>

Increment Looping

<?xml version="1.0" encoding="UTF-8"?>
<!-- IncrementLoopingUpTo: Recursively incrementing from j up to MAX. -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="text" encoding="UTF-8"/>
 
    <xsl:template match="/">
        <!-- Calling IncrementLoopingUpTo template -->
        <xsl:call-template name="IncrementLoopingUpTo">
          <xsl:with-param name="j" select="0"/>
          <xsl:with-param name="MAX" select="10"/>
        </xsl:call-template>
     </xsl:template>
 
    <!-- Implementation of IncrementLoopingUpTo template -->
    <!-- Recursively incrementing from j up to MAX -->
    <xsl:template name="IncrementLoopingUpTo">
        <xsl:param name="j" />
        <xsl:param name="MAX" />
 
        <xsl:if test="$j &lt;= $MAX">
            <xsl:value-of select="$j"/><xsl:text>&#xa;</xsl:text>
 
            <!-- Increment -->
             <xsl:call-template name="IncrementLoopingUpTo">
                <xsl:with-param name="j"><xsl:value-of select="$j + 1"/></xsl:with-param>
                <xsl:with-param name="MAX"><xsl:value-of select="$MAX"/></xsl:with-param>
            </xsl:call-template>
 
        </xsl:if>
    </xsl:template>
 
</xsl:stylesheet>

Nested Loops

<?xml version="1.0" encoding="UTF-8"?>
<!-- Nested Looping -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="text" encoding="UTF-8"/>
 
    <xsl:template match="/">
        <!-- Calling NestedLooping template -->
        <xsl:call-template name="NestedLooping">
          <xsl:with-param name="i1"        select="2"/>
          <xsl:with-param name="MAX1" select="4"/>
          <xsl:with-param name="i2"        select="3"/>
          <xsl:with-param name="MAX2" select="5"/>
        </xsl:call-template>
     </xsl:template>
 
    <!-- ========== Implementation of NestedLooping template ========== -->
    <xsl:template name="NestedLooping">
        <xsl:param name="i1"/>
        <xsl:param name="MAX1"/>
        <xsl:param name="i2"/>
        <xsl:param name="MAX2"/>
 
        <xsl:if test="$i1 &lt;= $MAX1">
            <!-- Call nested loop(Child Loop) -->
            <xsl:call-template name="ChildLoop">
                <xsl:with-param name="CarryNumber"><xsl:value-of select="$i1"/></xsl:with-param>
                <xsl:with-param name="i"><xsl:value-of select="$i2"/></xsl:with-param>
                <xsl:with-param name="MAX"><xsl:value-of select="$MAX2"/></xsl:with-param>
            </xsl:call-template>                
 
            <!-- Call itself(NestedLooping) with $i++ -->
            <xsl:call-template name="NestedLooping">
                <xsl:with-param name="i1"><xsl:value-of select="$i1 + 1"/></xsl:with-param>
                <xsl:with-param name="MAX1"><xsl:value-of select="$MAX1"/></xsl:with-param>
                <xsl:with-param name="i2"><xsl:value-of select="$i2"/></xsl:with-param>
                <xsl:with-param name="MAX2"><xsl:value-of select="$MAX2"/></xsl:with-param>
            </xsl:call-template>            
        </xsl:if>
 
    </xsl:template>
 
    <!-- Child Loop -->
    <xsl:template name="ChildLoop">
        <xsl:param name="CarryNumber"/>
        <xsl:param name="i"/>
        <xsl:param name="MAX"/>
 
        <xsl:if test="$i &lt;= $MAX">
            <xsl:text>Root Loop Index: </xsl:text><xsl:value-of select="$CarryNumber"/><xsl:text>  =&gt;  </xsl:text>
            <xsl:text>Child Loop Index: </xsl:text>   <xsl:value-of select="$i"/><xsl:text>&#xa;</xsl:text>
 
            <!-- Increment ChildLoop's $i -->
             <xsl:call-template name="ChildLoop">
                <xsl:with-param name="CarryNumber"><xsl:value-of select="$CarryNumber"/></xsl:with-param>
                <xsl:with-param name="i"><xsl:value-of select="$i + 1"/></xsl:with-param>
                <xsl:with-param name="MAX"><xsl:value-of select="$MAX"/></xsl:with-param>
            </xsl:call-template>
        </xsl:if>
 
    </xsl:template>
 
</xsl:stylesheet>

Remove Element Without Child Nodes

<?xml version="1.0" encoding="UTF-8"?>
<!-- Remove Element Without Child Nodes -->
<!-- Only the 1st level is removed. -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="*">
        <xsl:if test="count(@*) &gt; 0 or count(node()) &gt; 0">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>
    <xsl:template match="@* | text()">
        <xsl:copy/>
    </xsl:template>
</xsl:stylesheet>

Remove Namespace

<?xml version="1.0" encoding="UTF-8"?>
<!-- Remove Namespace -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="*">
      <!-- remove element prefix (if any) -->
      <xsl:element name="{local-name()}">
        <!-- process attributes -->
        <xsl:for-each select="@*">
          <!-- remove attribute prefix (if any) -->
          <xsl:attribute name="{local-name()}">
            <xsl:value-of select="."/>
          </xsl:attribute>
        </xsl:for-each>
        <xsl:apply-templates/>
      </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Remove attributes or / and nodes

<?xml version="1.0" encoding="UTF-8"?>
<!-- Remove unwanted attributes or/and nodes -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
 
    <!-- Copy everything -->
    <xsl:template match="@*|node()|text()|comment()|processing-instruction()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()|text()|comment()|processing-instruction()"/>
        </xsl:copy>
    </xsl:template>
 
    <!-- To remove attributes or nodes, simply write a matching template that doesn't do anything. Therefore, it is removed -->
    <xsl:template match="//d"/>                       <!-- Remove all d nodes -->
    <xsl:template match="//node()/node()/node()/c"/>  <!-- Remove all c nodes that have 3 ancestors -->
    <xsl:template match="@id_1"/>                     <!-- Remove all id_1 attributes -->
 
</xsl:stylesheet>

Using XPath's text() function

The XML file:

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <textarea name="123">a</textarea>
  <textarea name="123">b</textarea>
  <textarea name="123">a</textarea>
  <textarea name="123">c</textarea>
  <textarea name="555">a</textarea>
</root>

The XSL below shows how to use text() in XPath:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="text" encoding="UTF-8" />
 
  <xsl:template match="/">
    <xsl:for-each select="//textarea[@name='123' and text()='a']">
      <xsl:text>I found my XPath.&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>
 
</xsl:stylesheet>

xmllint

# Validate xml using XSD file.
xmllint --noout --schema yourSchema.xsd yourXmlFile.xml >> results.log 2>&1

xsltproc

Description xsltproc is a command line XSLT processor. It is used to apply XSL sheet on XML files.

Commands To get all options available for xsltproc, run

xsltproc --help

Apply XSL sheet on XML file:

xsltproc Data.xsl Data.xml > Data.output.txt

Passing string parameters into XSL sheet:

xsltproc --stringparam filename "my filename string" Data.xsl Data.xml > Data.output.txt

In Data.xsl, you can get the value of the filename parameter as follows: <xsl:value-of select="$filename"/>.