Miscellaneous Techniques
 

1. Counting no of times if condition returns true in xsl:for-each loop

The following question was posted on the newsgroup, microsoft.public.xsl.

I'm using a xsl:if inside a for-each. I want to track the number of times the if test attribute returns true. How can this be done?

Solution to this problem is

Writing logic to count within for-each may not be necessary. You can write the suitable XPath for select attribute of xsl:for-each (using predicates).
For e.g. xsl:for-each select="node-set[][].." Each predicate will act like a if condition.. And a suitable node-set will be available for processing.

I feel the above approach may solve the problem.

If you really need to count no of times if within a for-each returns true, here is one approach:

<xsl:variable name="times">
  <xsl:for-each select="node-set-expr">
    <xsl:if test="some-condition">
       1
    </xsl:if>
  </xsl:for-each>
</xsl:variable>

<xsl:value-of select="string-length($times) - string-length(translate($times,'1',''))" />

The approach used is:

Enclose for-each in a variable. If the condition within for-each is true, output 1 (or any other character).

Now count the no of 1s in the variable, which is the answer.
 

2. Searching for the longest path in a tree

The following question was asked on XSL-List.

Given this XML file:

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

<corpus>
    <phrase id="syntax_7"> <!-- Diskurs-Baum -->
        <phrase id="syntax_5"> <!-- Satz 1 -->
            <phrase id="syntax_1" cat="NP" func="PD" case="NOM">
                <tok id="tok_1" lemma="dies" pos="PDS">Dies</tok>
                <tok id="tok_2" lemma="sein" pos="VAFIN">ist</tok>
            </phrase>

            <phrase id="syntax_2" cat="NP" func="SB" case="NOM">
                <tok id="tok_3" lemma="eine" pos="ART">ein</tok>
                <tok id="tok_4" lemma="Beispielsatz" pos="NN">Beispielsatz</tok>
            </phrase>

            <tok id="tok_5" pos="$.">.</tok>
        </phrase>

        <phrase id="syntax_5"> <!-- Satz 2 -->
            <tok id="tok_6" lemma="und" pos="KON">Und</tok>
            <tok id="tok_7" lemma="hier" pos="ADV">hier</tok>
            <tok id="tok_8" lemma="folgen" pos="VVFIN">folgt</tok>

            <phrase id="syntax_3" cat="NP" func="IOBJ" case="DAT">
                <tok id="tok_9" lemma="er" pos="PPER">ihm</tok>
            </phrase>

            <tok id="tok_10" lemma="zum Beispiel" pos="ADV">z.B.</tok>
            <tok id="tok_11" lemma="noch" pos="ADV">noch</tok>

            <phrase id="syntax_4" cat="NP" func="SB" case="NOM">
                <tok id="tok_12" lemma="eine" pos="ART">ein</tok>
                <tok id="tok_13" lemma="weiter" pos="ADJA">weiterer</tok>
                <tok id="tok_14" lemma="Beispielsatz" pos="NN">Beispielsatz</tok>
            </phrase>

            <tok id="tok_15" pos="$.">.</tok>
        </phrase>   
    </phrase>
</corpus>

I want to have the  maximal depth of the tree. In the example XML it is 4.

The stylesheet for this problem is:

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

<xsl:output method="text" />

<xsl:template match="/">
  <xsl:call-template name="max-depth">
    <xsl:with-param name="node-set" select="//*" />
  </xsl:call-template>
</xsl:template>

<xsl:template name="max-depth">
  <xsl:param name="node-set" />
 
  <xsl:for-each select="$node-set">
    <xsl:sort select="count(ancestor::*)" order="descending" data-type="number" />
    <xsl:if test="position() = 1">
      <xsl:value-of select="count(ancestor::*)" />
    </xsl:if> 
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
 

3. Testing two XML documents for equality

I thought.., can we write a XSLT 1.0 stylesheet (without using extension functions) to compare two XML documents?

I presented this technique at XSL-List:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
 <xsl:output method="text" /> 
 
 <!-- parameter for "ignoring white-space only text nodes" during comparison -->
 <!-- if iws='y', "white-space only text nodes" will not be considered during comparison  -->
 <xsl:param name="iws" />
 
 <xsl:variable name="doc1" select="document('file1.xml')" />
 <xsl:variable name="doc2" select="document('file2.xml')" />
 
 <xsl:template match="/">
 
    <!-- store hash of 1st document into a variable; it is concatenation of name and values of all nodes -->
    <xsl:variable name="one">
      <xsl:for-each select="$doc1//@*">
        <xsl:sort select="name()" />
        <xsl:variable name="expr">
             <xsl:call-template name="constructXPathExpr">
                 <xsl:with-param name="node" select=".." />
                 <xsl:with-param name="xpath" select="name(..)" />
             </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="concat($expr,'/@',name(),':',.)" />:<xsl:value-of select="count(../ancestor-or-self::node() | ../preceding::node())" />
      </xsl:for-each>
      <xsl:choose>
         <xsl:when test="$iws='y'">
           <xsl:for-each select="$doc1//node()[not(normalize-space(self::text()) = '')]">
             <xsl:variable name="expr">
                 <xsl:call-template name="constructXPathExpr">
                    <xsl:with-param name="node" select="ancestor-or-self::*[1]" />
                    <xsl:with-param name="xpath" select="name(ancestor-or-self::*[1])" />
                 </xsl:call-template>
             </xsl:variable>
             <xsl:value-of select="concat($expr,'/',name(),':',.)" />:<xsl:value-of select="count(ancestor-or-self::node() | preceding::node())" /> 
           </xsl:for-each>
         </xsl:when>
         <xsl:otherwise>
           <xsl:for-each select="$doc1//node()">
              <xsl:variable name="expr">
                  <xsl:call-template name="constructXPathExpr">
                      <xsl:with-param name="node" select="ancestor-or-self::*[1]" />
                      <xsl:with-param name="xpath" select="name(ancestor-or-self::*[1])" />
                  </xsl:call-template>
             </xsl:variable>
             <xsl:value-of select="concat($expr,'/',name(),':',.)" />:<xsl:value-of select="count(ancestor-or-self::node() | preceding::node())" /> 
           </xsl:for-each>
         </xsl:otherwise>
      </xsl:choose>
    </xsl:variable> 
   
    <!-- store hash of 2nd document into a variable; it is concatenation of name and values of all nodes -->
    <xsl:variable name="two">
      <xsl:for-each select="$doc2//@*">
        <xsl:sort select="name()" />
        <xsl:variable name="expr">
            <xsl:call-template name="constructXPathExpr">
                 <xsl:with-param name="node" select=".." />
                 <xsl:with-param name="xpath" select="name(..)" />
            </xsl:call-template>
         </xsl:variable>
         <xsl:value-of select="concat($expr,'/@',name(),':',.)" />:<xsl:value-of select="count(../ancestor-or-self::node() | ../preceding::node())" /> 
      </xsl:for-each>
      <xsl:choose>
         <xsl:when test="$iws='y'">
           <xsl:for-each select="$doc2//node()[not(normalize-space(self::text()) = '')]">
                 <xsl:variable name="expr">
                     <xsl:call-template name="constructXPathExpr">
                         <xsl:with-param name="node" select="ancestor-or-self::*[1]" />
                         <xsl:with-param name="xpath" select="name(ancestor-or-self::*[1])" />
                    </xsl:call-template>
                 </xsl:variable>
                 <xsl:value-of select="concat($expr,'/',name(),':',.)" />:<xsl:value-of select="count(ancestor-or-self::node() | preceding::node())" /> 
           </xsl:for-each>
         </xsl:when>
         <xsl:otherwise>
           <xsl:for-each select="$doc2//node()">
                <xsl:variable name="expr">
                    <xsl:call-template name="constructXPathExpr">
                        <xsl:with-param name="node" select="ancestor-or-self::*[1]" />
                        <xsl:with-param name="xpath" select="name(ancestor-or-self::*[1])" />
                    </xsl:call-template>
                </xsl:variable>
                <xsl:value-of select="concat($expr,'/',name(),':',.)" />:<xsl:value-of select="count(ancestor-or-self::node() | preceding::node())" /> 
           </xsl:for-each>
         </xsl:otherwise>
      </xsl:choose>
    </xsl:variable> 
    <xsl:choose>
      <xsl:when test="$one = $two">
          Equal
      </xsl:when>
      <xsl:otherwise>
          Not equal       
      </xsl:otherwise>
    </xsl:choose>
 </xsl:template>
 
 <!-- template to construct an XPath expression, for a given element node -->
 <xsl:template name="constructXPathExpr">
   <xsl:param name="node" />
   <xsl:param name="xpath" />
     
   <xsl:choose>      
     <xsl:when test="$node/parent::*">
       <xsl:call-template name="constructXPathExpr">
            <xsl:with-param name="node" select="$node/parent::*" />
            <xsl:with-param name="xpath" select="concat(name($node/parent::*),'/',$xpath)" />
       </xsl:call-template>
     </xsl:when>
     <xsl:otherwise>
       <xsl:value-of select="concat('/',$xpath)" />
     </xsl:otherwise>
   </xsl:choose>
 </xsl:template>

</xsl:stylesheet>

(Thanks to David Carlisle who suggested many improvements)

XSLT 2.0 has a function deep-equal, which can be used to perform the similar task (Thanks to Michael Kay for suggesting this).

Here is a XSLT 2.0 stylesheet for this problem:

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

<xsl:output method="text" />

 <xsl:variable name="doc1" select="document('file1.xml')" />
 <xsl:variable name="doc2" select="document('file2.xml')" />

<xsl:template match="/">
    <xsl:choose>
        <xsl:when test="deep-equal($doc1, $doc2)">
            Equal
        </xsl:when>
        <xsl:otherwise>
            Not equal
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>
 

4. Work with different trees

The following question was asked on microsoft.public.xsl Newsgroup.

I have input xml like this:

<root>
    <part1>
        <a>
            <b>AA</b><b>BB</b>
        </a>
        <a>
            <b>EE</b><b>FF</b><b>GG</b>
        </a>
    </part1>
    <part2>
        <w>
            <ss>a</ss><ss>b</ss><ss>c</ss><ss>d</ss>
        </w>
        <w>
            <ss>e</ss><ss>f</ss><ss>g</ss>
        </w>
    </part2>
</root>

I need such output:

<part12>
   <res>
        <r>a AA</r>
        <r>b BB</r>
        ...
        <r>h HH</r>
   <res>
<part12>

I need to concatenate values from different trees. i.e. how to get the value of corresponding element from another tree.

The stylesheet for this problem is:

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

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

<xsl:template match="/root">
    <part12>
        <res>
            <!-- merging part1/a[1]/b and part2/w[1]/ss -->
            <xsl:choose>
                <xsl:when test="count(part1/a[1]/b) &gt; count(part2/w[1]/ss)">
                    <xsl:for-each select="part2/w[1]/ss">
                        <xsl:variable name="pos" select="position()" />
                        <r><xsl:value-of select="." /><xsl:text> </xsl:text><xsl:value-of select="../../../part1/a[1]/b[$pos]" /></r>
                    </xsl:for-each>
                    <xsl:for-each select="part1/a[1]/b[position() &gt; count(part2/w[1]/ss)]">
                        <r><xsl:value-of select="." /></r>
                    </xsl:for-each>
                </xsl:when>
                <xsl:when test="count(part1/a[1]/b) &lt; count(part2/w[1]/ss)">
                    <xsl:for-each select="part1/a[1]/b">
                        <xsl:variable name="pos" select="position()" />
                        <r><xsl:value-of select="../../../part2/w[1]/ss[$pos]" /><xsl:text> </xsl:text><xsl:value-of select="." /></r>
                    </xsl:for-each>
                    <xsl:for-each select="part2/w[1]/ss[position() &gt; count(../../../part1/a[1]/b)]">
                        <r><xsl:value-of select="." /></r>
                    </xsl:for-each>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:for-each select="part1/a[1]/b">
                        <xsl:variable name="pos" select="position()" />
                        <r><xsl:value-of select="../../../part2/w[1]/ss[$pos]" /><xsl:text> </xsl:text><xsl:value-of select="." /></r>
                    </xsl:for-each>
                </xsl:otherwise>
            </xsl:choose>

            <!-- merging part1/a[2]/b and part2/w[2]/ss -->
            <xsl:choose>
                <xsl:when test="count(part1/a[2]/b) &gt; count(part2/w[2]/ss)">
                    <xsl:for-each select="part2/w[2]/ss">
                        <xsl:variable name="pos" select="position()" />
                        <r><xsl:value-of select="." /><xsl:text> </xsl:text><xsl:value-of select="../../../part1/a[2]/b[$pos]" /></r>
                    </xsl:for-each>
                    <xsl:for-each select="part1/a[2]/b[position() &gt; count(part2/w[2]/ss)]">
                        <r><xsl:value-of select="." /></r>
                    </xsl:for-each>
                </xsl:when>
                <xsl:when test="count(part1/a[2]/b) &lt; count(part2/w[2]/ss)">
                    <xsl:for-each select="part1/a[2]/b">
                        <xsl:variable name="pos" select="position()" />
                        <r><xsl:value-of select="../../../part2/w[2]/ss[$pos]" /><xsl:text> </xsl:text><xsl:value-of select="." /></r>
                    </xsl:for-each>
                    <xsl:for-each select="part2/w[2]/ss[position() &gt; count(../../../part1/a[2]/b)]">
                        <r><xsl:value-of select="." /></r>
                    </xsl:for-each>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:for-each select="part1/a[2]/b">
                        <xsl:variable name="pos" select="position()" />
                        <r><xsl:value-of select="../../../part2/w[2]/ss[$pos]" /><xsl:text> </xsl:text><xsl:value-of select="." /></r>
                    </xsl:for-each>
                </xsl:otherwise>
            </xsl:choose>
      </res>
    </part12>
</xsl:template>

</xsl:stylesheet>


5. Counting related nodes

The following question was asked on XSL-List.

Given the xml:

<vs>
  <ve pos="1"></ve>
  <ve pos="1.1"></ve>
  <ve pos="1.1.1"></ve>
  <ve pos="1.1.1.1"></ve>
  <ve pos="1.1.1.2"></ve>
  <ve pos="1.1.1.3"></ve>
  <ve pos"1.2"></ve>
  <ve pos="1.2.1"></ve>
  <ve pos="1.2.1.1"></ve>
  <ve pos="2"></ve>
  <ve pos="2.1"></ve>
  <ve pos="2.1.1"></ve>
  <ve pos="2.1.1.1"></ve>
 </vs>
  
And given that I am starting on a node with pos = 1  (or 2 or 3....), how do I count the nodes which have position with 3 dots only  (e.g. pos = 1.1.2.1?) and start with the current node position.

The stylesheet for this problem is:

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

<xsl:output method="text" />

<xsl:variable name="startpos" select="'1'" />

<xsl:template match="/vs">
    <xsl:value-of select="count(ve[@pos=$startpos]/(self::ve | following-sibling::ve)[(string-length(@pos) - string-length(translate(@pos,'.',''))) = 3])" />
</xsl:template>

</xsl:stylesheet>


6.
Filtering data sets

The following question was asked on XSL-List.

I am trying to filter out data sets that do not have any data in the column numbers specified in "header" of the xml. The header specifies that columns 1, 2, 3 and 7 are crucial in this particular instance and must not be empty. The values differ from time to time (I cannot hardcode the numbers in the xsl), but the element names in "header" (AAA, BBB etc.) are static for this type of data).
 
I need to test if the column numbers specified in the header are non-empty for each each row. I have tried to assign the values of the header data to variables and do something like: for each row, if test not(column[$_aaa]='') create the element. This, however, I cannot get to work.

The stylesheet for this problem is:

Here is a generic solution I wrote. There is a downside though; it relies on node-set function..

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">

<xsl:output method="xml" indent="yes" encoding="UTF-8"/>

<xsl:template match="/root">
  <root>
    <xsl:variable name="rtf">
       <xsl:for-each select="row">
            <xsl:variable name="curr_row" select="."/>
            <xsl:variable name="x">
                <xsl:for-each select="column">
                   <xsl:if test="position() = /root/header/*">
                      <xsl:if test="normalize-space(.) = ''">
                         1
                      </xsl:if>
                   </xsl:if>
                </xsl:for-each>
             </xsl:variable>
             <xsl:if test="not(contains($x, '1'))">
                <success>
                   <xsl:for-each select="/root/header/*">
                       <xsl:element name="{name()}">
                           <xsl:value-of select="$curr_row/column[position() = current()]"/>
                       </xsl:element>
                   </xsl:for-each>
                </success>
             </xsl:if>
       </xsl:for-each>
   </xsl:variable>
   <flow>
      <total_in><xsl:value-of select="count(row)" /></total_in>
      <total_out><xsl:value-of select="count(row) - count(msxsl:node-set($rtf)/*)" /></total_out>
   </flow>
   <xsl:copy-of select="msxsl:node-set($rtf)/*"/>
</root>
</xsl:template>

</xsl:stylesheet>
 

7. Modifying XML document using information from another XML document

The following question was asked on XSL-List.

I've an input doc containing a number of modify-attr elements:

<modify>
  <modify-attr attr-name = "abc"/>
  <modify-attr attr-name = "123"/>
  <modify-attr attr-name = "789"/>
</modify>
 
I have another document containing a list of prohibited attributes:

<exclude-list>
  <exclude-attr>123</exclude-attr>
</exclude-list>

I want to iterate over my input doc and copy all those modify-attr elements that are NOT in the exclude list to the output.

The stylesheet for this problem is:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
<xsl:output method="xml" indent="yes" />
 
<xsl:variable name="exclude" select="document('exclude.xml')" />
 
<xsl:template match="node() | @*">
  <xsl:copy>
    <xsl:apply-templates select="node() | @*" />
  </xsl:copy>
</xsl:template>
 
<xsl:template match="*[@attr-name = $exclude/exclude-list/exclude-attr]" />
 
</xsl:stylesheet>

This is a modified identity stylesheet. The 2nd template excludes those elements, whose attr-name attribute has value matching that in exclude file.

Jay Bryant provided the following answer:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:variable name="exclude" select="document('excludedoc.xml')"/>
 
  <xsl:template match="modify">
    <modify>
      <xsl:apply-templates/>
    </modify>
  </xsl:template>

  <xsl:template match="modify-attr">
    <xsl:variable name="name" select="@attr-name"/>
    <xsl:if test="not($exclude/exclude-list/exclude-attr[.=$name])">
      <xsl:copy-of select="."/>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

David Carlisle provided this suggestion:

<xsl:template match="modify">
 <xsl:copy-of select="modify-attr[not(@attr-name=document('excludedoc')/exclude-list/exclude-attr)]"/>
</xsl:template>
 

8. Stripping out non-numbers

The following question was asked on XSL-List.

I need to strip out non-numerical values from a string.  Here is a sample input value:  TUV0062. And what I want is :  0062  (or just 62)

The stylesheet for this problem is:

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

<xsl:output method="text" />
 
<xsl:variable name="includechars" select="'0123456789'" />
 
<xsl:template match="/">
  <xsl:variable name="teststring" select="'TUV0062'" />
   
  <xsl:value-of select="translate($teststring, translate($teststring,$includechars,''), '')" />
</xsl:template>
 
</xsl:stylesheet>

G. Ken Holman provided this answer:

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

<xsl:output method="text"/>

<xsl:template match="/">
  <xsl:variable name="target" select="'TUV0062'"/>

  <xsl:value-of select="number(
                            translate($target,
                            translate($target,'0123456789',''),''))"/>
</xsl:template>

</xsl:stylesheet>

Dimitre Novatchev said:

This is the "double-translate" technique first proposed on this list (to the best of my knowledge) by Mike Kay.

The FXSL template/function

    str-filter

has been shown to perform better than the double-translate technique on large strings:

http://www.biglist.com/lists/xsl-list/archives/200203/msg01524.html


9. Finding duplicates on multiple elements

The following question was asked on microsoft.public.xsl Newsgroup.

What I need is something that will use values from two elements to determine if the values are duplicates. Consider following XML:

<DOCUMENTS>
  <DOCUMENT>
    <PATH>/abc/</PATH>
    <FILENAME>xyz.htm</PATH>
  </DOCUMENT>
  <DOCUMENT>
    <PATH>/abc/</PATH>
    <FILENAME>abc.htm</PATH>
  </DOCUMENT>
  <DOCUMENT>
    <PATH>/efg/</PATH>
    <FILENAME>xyz.htm</FILENAME>
  </DOCUMENT>
  <DOCUMENT>
    <PATH>/abc/</PATH>
    <FILENAME>xyz.htm</FILENAME>
  </DOCUMENT>
</DOCUMENTS>

Now I would like the output to be:

/abc/xyz.htm has a duplicate
/abc/abc.htm has no duplicate
/efg/xyz.htm has no duplicate
/abc/xyz.htm has no duplicate

The stylesheet for this problem is:

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

<xsl:key name="by-path-and-filename" match="DOCUMENT" use="concat(PATH,' ', FILENAME)" />
 
<xsl:template match="/DOCUMENTS">
   <xsl:for-each select="DOCUMENT">    
     <xsl:if test="count(key('by-path-and-filename', concat(PATH,' ', FILENAME))) > 1">
         <xsl:value-of select="concat(PATH, FILENAME)" /> has a duplicate<xsl:text>&#xa;</xsl:text>
     </xsl:if>
     <xsl:if test="count(key('by-path-and-filename', concat(PATH,' ', FILENAME))) = 1">
         <xsl:value-of select="concat(PATH, FILENAME)" /> has no duplicate<xsl:text>&#xa;</xsl:text>
     </xsl:if>
   </xsl:for-each>
</xsl:template>
 
</xsl:stylesheet>

An interesting question was asked (by the person who posted this question). Would it not be more effiecient to use a choose when otherwise in place of two if's which call functions?

I reasoned..
I feel you are right. Using "choose when otherwise" will be more efficient  than two if's.. as it will avoid two calculations of count(key..
 

10. Strip non Alpha-numeric characters

The following question was asked on XSL-List.

I was wondering if there is any way possible of stripping any non-alphanumeric characters from an attribute. ie keep anything that is A-Z/0-9 and strip all other characters like ",*-+. etc etc?

The stylesheet for this problem is:

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

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

<xsl:variable name="allowedchars" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'" />

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

<xsl:template match="@*[string-length(translate(., $allowedchars, '')) &gt; 0]">
    <xsl:attribute name="{name()}">
        <xsl:value-of select="translate(., translate(., $allowedchars, ''), '')" />
    </xsl:attribute>
</xsl:template>

</xsl:stylesheet>

This is a modified identity stylesheet.

For e.g., when the above XSLT is applied to XML:

<?xml version="1.0"?>
<root>
  <a x="123ABC+-" u="MNO" />
  <b y="ABC12" v="PQR+-" />
  <c z="+-1" w="WX" />
</root>

The output produced is:

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <a x="123ABC" u="MNO"/>
    <b y="ABC12" v="PQR"/>
    <c z="1" w="WX"/>
</root>
 

11. XSL Sort and Copy

The following question was asked on microsoft.public.xsl Newsgroup.

I am trying to copy an xml document into another, sorted by the attribute of one of the child nodes. The xml is:

<?xml version="1.0" encoding="UTF-8"?>
<WFC>
   <Response Status="Success" Timeout="1800" PersonKey="13" Object="System" Action="Logon" PersonNumber="ALL ACCOUNTS LI" Username="All Accounts">
   </Response>
   <Response Status="Success" Action="RunQuery">
      <Result FullName="A" PersonNumber="2709"/>
      <Result FullName="D" PersonNumber="4054"/>
      <Result FullName="B" PersonNumber="4108"/>     
   </Response>
   <Response Status="Success" Object="System" Action="Logoff">
   </Response>
</WFC>

I want the exact structure to be copied to another xml doc, however sorted by FullName attribute value of the Result Node.

The stylesheet for this problem is:

(by modified the identity stylesheet)

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

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

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

<xsl:template match="Response">
  <Response>
    <xsl:copy-of select="@*" />
    <xsl:for-each select="Result">
       <xsl:sort select="@FullName" />
       <xsl:copy-of select="." />
    </xsl:for-each>
  </Response>
</xsl:template>

</xsl:stylesheet>


12. Transforming Flat XML to Hierarchical XML

The following question was asked on XSL-List.

I have an XML which is flat:

<?xml version="1.0" encoding="utf-8" ?>
<ns>
  <RECSETNAME>
    <Record>
      <keyfieldValue>HEADR</keyfieldValue>
      <fieldValue>CDJOB</fieldValue>
      <fieldValue>TRA</fieldValue>
    </Record>
    <Record>
      <keyfieldValue>TRANS</keyfieldValue>
      <fieldValue>DATA</fieldValue>
      <fieldValue>EXCHG</fieldValue>
      <fieldValue>EXCH</fieldValue>
    </Record>
    <Record>
      <keyfieldValue>MTPNT</keyfieldValue>
      <fieldValue>74842606</fieldValue>
    </Record>
    <Record>
      <keyfieldValue>ADDRS</keyfieldValue>
      <fieldValue>MTRPT</fieldValue>
      <fieldValue>BRITISH TELECOM</fieldValue>
    </Record>
    <Record>
      <keyfieldValue>ASSET</keyfieldValue>
      <fieldValue>INSTL</fieldValue>
      <fieldValue>METER</fieldValue>
    </Record>
    <Record>
      <keyfieldValue>METER</keyfieldValue>
      <fieldValue>T</fieldValue>
    </Record>
  </RECSETNAME>
</ns>

All the data is stored under the record structure. Based on the keyFieldValue I have to generate the target node.

My Target XML should look like:

<HEADR>
   <fieldValue>CDJOB</fieldValue>
   <fieldValue>TRA</fieldValue>
   <TRANS>
     <fieldValue>DATA</fieldValue>
     <fieldValue>EXCHG</fieldValue>
     <fieldValue>EXCH</fieldValue>
     <MTPNT>
       <fieldValue>74842606</fieldValue>
       <ADDRS>
          <fieldValue>MTRPT</fieldValue>
          <fieldValue>BRITISH TELECOM</fieldValue>
       </ADDRS>
       <ASSET>
          <fieldValue>INSTL</fieldValue>
          <fieldValue>568</fieldValue>
          <METER>
            <fieldValue>T</fieldValue>
          </METER>
       </ASSET>
     <MTPNT>
   </TRANS>
</HEADR>

The stylesheet for this problem is:

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

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

<xsl:template match="/">
  <xsl:call-template name="xyz">
    <xsl:with-param name="x" select="//Record[1]" />
  </xsl:call-template>
</xsl:template>

<xsl:template name="xyz">
  <xsl:param name="x" />
 
  <xsl:if test="$x">
    <xsl:element name="{$x/keyfieldValue}">
      <xsl:for-each select="$x/fieldValue">
        <fieldValue><xsl:value-of select="." /></fieldValue>
      </xsl:for-each>
      <xsl:call-template name="xyz">
        <xsl:with-param name="x" select="$x/following-sibling::Record[1]" />
      </xsl:call-template>
    </xsl:element>
  </xsl:if> 
</xsl:template>

</xsl:stylesheet>

A changed requirement is.. If the XML file is:

<ns>
  <RECSETNAME>
    <Record>
        <keyfieldValue>HEADR</keyfieldValue>
        <fieldValue>CDJOB</fieldValue>
        <fieldValue>TRA</fieldValue>
    </Record>
    <Record>
        <keyfieldValue>TRANS</keyfieldValue>
        <fieldValue>DATA</fieldValue>
        <fieldValue>EXCHG</fieldValue>
        <fieldValue>EXCH</fieldValue>
    </Record>
    <Record>
        <keyfieldValue>MTPNT</keyfieldValue>
        <fieldValue>74842606</fieldValue>
    </Record>
    <Record>
        <keyfieldValue>ADDRS</keyfieldValue>
        <fieldValue>MTRPT</fieldValue>
        <fieldValue>BRITISH TELECOM</fieldValue>
    </Record>
    <Record>
        <keyfieldValue>ASSET</keyfieldValue>
        <fieldValue>INSTL</fieldValue>
        <fieldValue>METER</fieldValue>
    </Record>
    <Record>
        <keyfieldValue>METER</keyfieldValue>
        <fieldValue>T</fieldValue>
    </Record>
   <Record>
        <keyfieldValue>APPOINTMENT</keyfieldValue>
        <fieldValue>T</fieldValue>
    </Record>
    <Record>
        <keyfieldValue>NAME</keyfieldValue>
        <fieldValue>TEST</fieldValue>
        <fieldValue>BRITISH TELECOM</fieldValue>
    </Record>
    <Record>
        <keyfieldValue>ADDRS</keyfieldValue>
        <fieldValue>NAME</fieldValue>
        <fieldValue>BRITISH TELECOM</fieldValue>
    </Record>
  </RECSETNAME>
</ns>

(now two additional elements are at the end)

The desired output is:

<?xml version="1.0" encoding="UTF-8"?>
<HEADR>
   <fieldValue>CDJOB</fieldValue>
   <fieldValue>TRA</fieldValue>
   <TRANS>
      <fieldValue>DATA</fieldValue>
      <fieldValue>EXCHG</fieldValue>
      <fieldValue>EXCH</fieldValue>
      <MTPNT>
         <fieldValue>74842606</fieldValue>
         <ADDRS>
            <fieldValue>MTRPT</fieldValue>
            <fieldValue>BRITISH TELECOM</fieldValue>
            <ASSET>
               <fieldValue>INSTL</fieldValue>
               <fieldValue>METER</fieldValue>
               <METER>
                  <fieldValue>T</fieldValue>
               </METER>
            </ASSET>
         </ADDRS>
      </MTPNT>
      <APPOINTMENT>
        <keyfieldValue>APPOINTMENT</keyfieldValue>
        <fieldValue>T</fieldValue>
      </APPOINTMENT>
      <NAME>
        <keyfieldValue>NAME</keyfieldValue>
        <fieldValue>TEST</fieldValue>
        <fieldValue>BRITISH TELECOM</fieldValue>
        <ADDRESS>
          <keyfieldValue>ADDRS</keyfieldValue>
          <fieldValue>NAME</fieldValue>
          <fieldValue>BRITISH TELECOM</fieldValue>
        </ADDRESS>
      </NAME>
   </TRANS>
</HEADR>

The stylesheet for this problem is:

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

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

<xsl:template match="/">
  <xsl:element name="{//Record[1]/keyfieldValue}">
    <xsl:copy-of select="//Record[1]/*[not(self::keyfieldValue)]" />
    <xsl:element name="{//Record[2]/keyfieldValue}">
      <xsl:copy-of select="//Record[2]/*[not(self::keyfieldValue)]" />    
      <xsl:call-template name="xyz">
        <xsl:with-param name="x" select="//Record[3]" />
      </xsl:call-template>
      <APPOINTMENT>
        <xsl:copy-of select="//Record[keyfieldValue = 'APPOINTMENT']/*" />
      </APPOINTMENT>
      <xsl:for-each select="//Record[keyfieldValue = 'NAME']">
        <NAME>
          <xsl:copy-of select="*" />
          <ADDRESS>
            <xsl:copy-of select="following-sibling::Record[1]/*" />
          </ADDRESS>
        </NAME>
      </xsl:for-each>
    </xsl:element>
  </xsl:element>
</xsl:template>

<xsl:template name="xyz">
  <xsl:param name="x" />
 
  <xsl:if test="$x and not($x/keyfieldValue = 'APPOINTMENT') and not($x/keyfieldValue = 'NAME') and not(($x/keyfieldValue = 'ADDRS') and ($x/preceding-sibling::Record[1]/keyfieldValue = 'NAME'))">
    <xsl:element name="{$x/keyfieldValue}">
      <xsl:copy-of select="$x/fieldValue" />
      <xsl:call-template name="xyz">
        <xsl:with-param name="x" select="$x/following-sibling::Record[1]" />
      </xsl:call-template>
    </xsl:element>
  </xsl:if> 
</xsl:template>

</xsl:stylesheet>

I mentioned.. I have slightly changed the syntax (as compared to my previous stylesheet).. I am now using <xsl:copy-of instead of xsl:for-each (for syntactic convenience)..
 

13. Excluding some descendant elements

The following question was asked on XSL-List.

My XML doc has this basic structure:

<a>
  <b>
    <c>
      <!-- This is the section of interest -->
    </c>
  </b>
</a>

The <c> element can contain any combination of elements <d> through <z>. Elements <y> and <z> have special uses.

For instance, given this source:

<a><b><c>
  <d>
    <z>
    <g/>
  </d>
  <q>
    <r>
      <y/>
      <z/>
    </r>
    <y/>
  <q>
  <y>
</c></b></a>

I want the selection to contain this:

<a><b><c>
  <d>
    <g/>
  </d>
  <q>
    <r>
      <y/>
    </r>
  <q>
</c></b></a>

(<z> elements are gone, only the first <y> element remains)

I wish to handle <y> and <z> appearing at any depth in the descendant tree.

The stylesheet for this problem is:

(a variation of identity transform)

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

<xsl:output method="xml" indent="yes" />
   
<xsl:template match="node() | @*">
  <xsl:copy>
    <xsl:apply-templates select="node() | @*" /> 
  </xsl:copy>
</xsl:template>
 
<!-- include only 1st y , and exclude all z -->
<xsl:template match="y[ancestor::y or preceding::y] | z" />
 
</xsl:stylesheet>


Michael Kay provided following explanation:

I think you are a little confused between the terms "node-set" and "result-tree-fragment". (Not surprising, most people are).

You select a node-set using

<xsl:variable name="n" select="--- some path expression ---"/>

The result is a set of nodes - I actually prefer to think of it as a set of *references* to nodes. These are original nodes in the source document, and they retain their original position in the source document, which means for example that you can process each node in the set to ask how many ancestors it has.

You create a result tree fragment (or in 2.0 terminology a temporary tree) using

<xsl:variable name="n">
  -- some instructions ---
</xsl:variable>

The result is a new document. (The term "fragment" comes from DOM, and means a document that isn't constrained to have a single element at the top level.) The nodes in this tree are newly constructed nodes; they may be copies of nodes in the source tree (or not) but they have separate identity and have lost their relationships to other nodes in the source tree.

The example that you've given suggests that you do actually want to create a new tree that is a selective copy of the original tree. The way to do this is to walk the original tree applying templates. If a node is to be copied into the new tree, you apply the identity template, if it is to be removed, you apply an empty template, and of course you can also have templates that modify selected elements. So it looks something like this:

<xsl:variable name="subset">
  <xsl:apply-templates select="a" mode="subset"/>
</xsl:variable>

<!-- by default, an element is copied -->

<xsl:template match="*" mode="subset">
  <xsl:copy><xsl:copy-of select="@*"/><xsl:apply-templates mode="subset"/></xsl:copy>
</xsl:template>

<!-- I only want to include [only] the first <y> element that is contained within <c>, no matter where it occurs. There may be no <y> elements present.
-->

<xsl:template match="y[not(. is (ancestor::c//y)[1])]" mode="subset"/>

<!-- I want to exclude all <z> elements that are contained within <c>, no
  matter where they occur. Again, there may be none present. -->

<xsl:template match="z" mode="subset"/>

I used the XPath 2.0 "is" operator for comparing node identity here. The 1.0 equivalent of "A is B" is generate-id(A)=generate-id(B).


14. Using position() function rightly

The following question was asked on XSL-List.

With this xml structure in mind:

 <dfile>
  <df_column_names>
    <df_column name="employee_name" type="hyperlink">Name:</df_column>
    <df_column name="department" type="text">Department:</df_column>
    <df_column name="position" type="text">Position:</df_column>
  </df_column_names>
  <df_data_row>
    <data_key>1695</data_key>
    <df_data>Charles Hilditch</df_data>
    <df_data>Processing</df_data>
    <df_data>Mill Supervisor</df_data>
  </df_data_row>
 </dfile>

 I am trying to select the type attribute for a particular df_data element.
 
 <xsl:for-each select="./df_data">
  <xsl:value-of select="/dfile/df_column_names/df_column[position()]/@type"/>
 </xsl:for-each>
 
My algorithm is simply selecting the type attribute from df_column whose position is equal to current df_data element being processed.

I keep getting the type attribute for the first db_column name only.

Michael Kay provided following explanation:

When the value of a predicate [X] is an integer, it's a shorthand for [position()=X]. So E[position()] means E[position()=position()] which is the same as E[true()], or simply E. Your mistake is in forgetting that the context changes inside the square brackets. So you need to assign a variable to the value of position() outside the predicate, and then use the variable inside the predicate:

<xsl:variable name="pos" select="position">
<xsl:value-of select="x/y/z[$pos]"/>


15. Encoding Country Codes

The following question was asked on XSL-List.

I require XSLT support in amending the country codes to my resulting XML. I have all country names with relevant country codes in an external XML file (i.e., CountryCode.xml, given below), here I require all countries needs to be encoded in my each <affiliation> of the source xml, also please find below the resulting xml for your easy understanding.

CountryCode.xml

<allcountry>
  <country code="gb">England</country>
  <country code="in">India</country>
  <country code="us">USA</country>
<allcountry>

source.xml

<root>
  <para>Some text</para>
  <affiliation>Indian Institute of Technology, Chennai, India - 610545</affiliation>
  <affiliation>SOME INSTITUTION NAME, ENGLAND - 12245</affiliation>
  <affiliation>IEEE Institution, NY, USA</affiliation>
</root>

result.xml

<root>
  <affiliation>Indian Institute of Technology, Chennai, <country code="in">India</country> - 610545</affiliation>
  <affiliation>SOME INSTITUTION NAME, <country code="gb">ENGLAND</country> - 12245</affiliation>
  <affiliation>IEEE Institution, NY, <country code="us">USA</country></affiliation>
</root>

Please advise

The stylesheet for this problem is:

(modified identity stylesheet)

In this stylesheet, I have made following assumptions:

1) The CountryCode.xml file you posted specifies England. So I think source.xml should also specify England (not ENGLAND) . I tested by changing to England..
2) My stylesheet is replacing a "string pattern" (its first occurence) in <affiliation> tag with value from CountryCode.xml file..  

This is having following effect:

<affiliation>
  <country code="in">India</country>n Institute of Technology, Chennai, India - 610545
</affiliation>

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

<xsl:variable name="country" select="document('CountryCode.xml')" />
 
<xsl:template match="node() | @*">
  <xsl:copy>
    <xsl:apply-templates select="node() | @*" />
  </xsl:copy>
</xsl:template>
 
<xsl:template match="para" />
 
<xsl:template match="text()[not(normalize-space() = '')]">
   <xsl:variable name="temp" select="." />
  
   <xsl:for-each select="$country/allcountry/country[contains($temp, .)]">
     <xsl:value-of select="substring-before($temp, .)" />
     <country code="{@code}">
       <xsl:value-of select="." />
     </country>
     <xsl:value-of select="substring-after($temp, .)" />  
   </xsl:for-each>
  
</xsl:template>

 
</xsl:stylesheet>
 

16. Transforming XML into another XML format

The following question was asked on XSL-List.

I have following source XML file:

<root>
  <p>some text</p>
  <p>some text <i>italic text</i></p>
  <p>some text <b><i>bold+italic text</i> bold text</b></p>
  <p>some text <i>italic text <sub>subscript+italic text</sub></i> some text</p>
  <p>some text <b>bold text <i>Italic text</i> bold continues <i>x<inf>2</inf></i> bold continues</b></p>
</root>
 
I need to transform this to this XML format:

<root>
  <para>some text</para>
  <para>some text <render font-style="italic">italic text</render></para>
  <para>some text <render font-weight="bold" font-style="italic">bold+italic text</render><render font weight="bold"> bold text</render></para>
  <para>some text <render font-style="italic">italic text </render><render font-style="italic" baseline-shift="sub" font-size="smaller">subscript+italic text</render> some text</para>
  <para>some text <render font-weight="bold">bold text </render><render font-weight="bold" font style="italic">bold+Italic text</render><render font-weight="bold"> bold continues </render><render font-weight="bold" font-style="italic">x</render><render font-weight="bold" baseline-shift="sub" font-size="smaller">2</render><render font-weight="bold"> bold continues</render></para>
</root>

Michael Kay suggested this approach:

Interesting one. I think I would tackle this in the template that matches the text nodes, something like this:

<xsl:template match="para//i/text() | para//b/text() | para//inf/text()">
  <render>
     <xsl:if test="ancestor::i">
        <xsl:attribute name="font-style">italic</xsl:attribute>
     </xsl:if>
     <xsl:if test="ancestor::b">
        <xsl:attribute name="font-weight">bold</xsl:attribute>
     </xsl:if>
     <xsl:if test="ancestor::inf">
        <xsl:attribute name="baseline-shift">sub</xsl:attribute>
        <xsl:attribute name="font-size">smaller</xsl:attribute>
      </xsl:if>
      <xsl:value-of select="."/>
  </render>
</xsl:template>

(I think it should be <xsl:template match="p//i/text() | p//b/text() | p//inf/text()">.. It seems, Michael mistakingly wrote it.)

I suggested:

You may also try this.. This is a modified identity stylesheet.. I might have missed some template rules (I think I have).. But you may take the idea..

<?xml version="1.0"?>
<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="p">
    <para>
      <xsl:apply-templates />
    </para>
  </xsl:template>
  
  <xsl:template match="i">
    <render font-style="italic">
      <xsl:apply-templates />
    </render>
  </xsl:template>
 
  <xsl:template match="b/i">
    <render font-weight="bold" font-style="italic">
      <xsl:apply-templates />
    </render>
  </xsl:template>
 
  <xsl:template match="i/sub">
    <render font-style="italic" baseline-shift="sub" font-size="smaller">
      <xsl:apply-templates />
    </render>
  </xsl:template> 
   
  <xsl:template match="b/i/inf">
    <render font-weight="bold" baseline-shift="sub" font-size="smaller">
      <xsl:apply-templates />
    </render>
  </xsl:template>
 
  <xsl:template match="b">
    <xsl:apply-templates />
  </xsl:template>
 
  <xsl:template match="b/text()">
    <render font-weight="bold">
      <xsl:value-of select="." />
    </render>
  </xsl:template> 
 
</xsl:stylesheet>


17. Validate generated XML file to a new XML schema in XSL

The following question was asked on XSL-List.

Given this XML document:

<?xml version="1.0"?>
<Unit xmlns="http://www.xml.com/xml/unit" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xml.com/xml/unit/unit.xsd">
  <Units>
    <Unit_Code>5555</Unit_Code>
    <Unit_Name>System</Unit_Name>
  </Units>
</Unit>

I wish to display XML that will validate to a new XML schema file.

<?xml version="1.0"?>
  <Unit xmlns="http://www.xml.com/xml/unit" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.xml.com/xml/unit/newUnit.xsd">
    <Units>
      <Unit_Code>5555</Unit_Code>
      <Unit_Name>System</Unit_Name>
    </Units>
</Unit>

The stylesheet for this problem is:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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="@*[name() = 'xsi:schemaLocation']">
   <xsl:attribute name="xsi:schemaLocation">http://www.xml.com/xml/unit/newUnit.xsd</xsl:attribute>
</xsl:template>
 
</xsl:stylesheet>

This is tested with Saxon 6.5.3

Michael Kay provided following explanation:

XSLT 2.0 allows you to specify the validation of the generated XML file to be done as an intrinsic part of the transformation. The advantage of this is that the validation is done earlier (and it's done automatically).

Often it's possible to detect errors at the time the stylesheet is compiled, before you even run it against a source document; and where the errors are detected at run-time, you can get error messages that point to the line of code in the stylesheet that generated the bad output, rather than having to study the output of the schema processor which only tells you where the output itself is wrong. Try it out with Saxon-SA.

However, going back to your approach: Looking at your input and output, what you seem to be doing is a near-identity transform that changes the value of one attribute. There's a standard pattern for that:

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

<xsl:template match="@xsi:schemaLocation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <xsl:attribute name="xsi:schemaLocation">
    <xsl:text>http://www.xml.com/xml/unit/newUnit.xsd</xsl:text>
  </xsl:attribute>
</xsl:template>

Again, however, I'd question the design. Most schema processors allow you to say "validate document X against schema Y" - the location of the schema doesn't have to be hard-coded in the xsi:schemaLocation attribute.

Modifying the document to change the xsi:schemaLocation attribute should therefore be quite unnecessary.


18.
Handling mixed content elements (transforming XML to another format)

The following question was asked on XSL-List.

In the XML snippet below...

<paragraph>
  <bold>Actel</bold>
  (Sunnyvale, CA) will showcase its third-generation flash-based FPGA device, ProASIC3 (see <italic>page 105</italic>)—said to be the industry's lowest-cost FPGA, starting at $1.50. The company's Libero integrated
design environment and broad IP offerings will also be on exhibit. Free workshops and demonstrations will be offered throughout the show. (Booth #920, http://info.edu/47)
</paragraph>

...I applied the following template...

<xsl:template match="paragraph">
  <p>
    <xsl:if test="bold">
      <span style="font-weight: bold"><xsl:value-of select="bold" /></span>
    </xsl:if>
    <xsl:if test="italic">
      <span style="font-style: italic"><xsl:value-of select="italic" /></span>
    </xsl:if>
    <xsl:if test="text()">
      <xsl:value-of select="text()" />
    </xsl:if>
  </p>
</xsl:template>

...for which I received the following result...

<p>
  <span style="font-weight: bold">Actel </span><span style="font-style:italic">page 105</span>(Sunnyvale, CA) will showcase its third-generation flash-based FPGA device, ProASIC3 (see
</p>

In the source XML document, each paragraph may contain any number of <bold> and <italic> tags in any order. How can I modify the "paragraph" template to account for this?

The stylesheet for this problem is:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
<xsl:output method="html" indent="yes" />

<xsl:template match="/">
   <html>
     <head>
       <title/>
     </head>
     <body>
       <xsl:apply-templates />
     </body> 
   </html>
</xsl:template>
 
<xsl:template match="node() | @*">
   <xsl:copy>
     <xsl:apply-templates select="node() | @*" />
   </xsl:copy>
</xsl:template>
 
<xsl:template match="paragraph">
   <p>
     <xsl:apply-templates />
   </p>
</xsl:template>
 
<xsl:template match="bold">
   <span style="font-weight: bold">
     <xsl:apply-templates />
   </span>
</xsl:template>
 
</xsl:stylesheet>

The person who asked this question, had this query..

> If I understand you correctly, you've utilized an identity template. Very
> cool. Unfortunately, and I did not specify this in my original post, the
> structure of the XML document is about half narrative-like and half
> data-like. The identity template would be too broad to address it.

Michael Kay replied:

Then you can either use a special mode for the identity template, or you can constrain it to apply only within a paragraph by specifying

match="paragraph//*"


19. Extracting information from different places in XML (using predicates, axes and other constructs effectively)

The following question was asked on XSL-List.

I'm having trouble with a predicate selection (i.e. a conditional selection). For the following xml structure.

<company>
  <division DID="XXX">
      <projects>
          <project PID='XYZ'> (Project ID)
             <PNAME>Some Name</PNAME> <!-- (Project Name) -->
             <BUDGET>111</BUDGET>
             <assigns>
                 <assign refEID='XXX000'>  
                   <HOURS>12</HOURS>
                 </assign>
                 <assign refEID='XXX001'>
                   <HOURS>78</HOURS>
                 </assign>
             </assigns>
          </project>
          <project PID='XY1'>
             <PNAME>Some OtherName</PNAME>
             <BUDGET>112</BUDGET>
             <assigns>
                 <assign refEID='XXX000'>  
                   <HOURS>34</HOURS>
                 </assign>
                 <assign refEID='XXX002'>
                   <HOURS>1234</HOURS>
                 </assign>
             </assigns>
          </project>
          Etc..
      </projects>
      <employees>
          <employee EID='XXX000'>
            <ENAME>Joe Blow</ENAME>
            <OFFICE>1204</OFFICE>
            <BIRTHDATE>1924-08-01</BIRTHDATE>
          </employee>
          <employee EID='XXX001'>
            <ENAME>Joe Smith</ENAME>
            <OFFICE>1203</OFFICE>
            <BIRTHDATE>1930-08-01</BIRTHDATE>
          </employee>
          <employee EID='XXX002'>
            <ENAME>Joe Jerry</ENAME>
            <OFFICE>0003</OFFICE>
            <BIRTHDATE>1930-08-01</BIRTHDATE>
          </employee>             
      </employees>  
  </division>
Etc.. (there are multiple divisions with the same structure, employees can only work for one division and a project only belongs to a single division) 
</company>

I want to have a table that shows PID, PNAME, BUDGET and then the employee EID (unique identifier), the employee name (ENAME), OFFICE and HOURS spent on each project.

The output I want is:

Division Name = XXX
PID='XYZ'   PNAME=Some Name BUDGET=111     
EID= XXX000 ENAME= Joe Blow  OFFICE=1204 HOURS=12
EID= XXX001 ENAME= Joe Smith OFFICE=1203 HOURS=78

PID=' XY1'  PNAME= Some OtherName BUDGET=112
EID= XXX000 ENAME= Joe Blow  OFFICE=1204 HOURS=34
EID= XXX002 ENAME= Joe Jerry OFFICE=0003 HOURS=1234

This above output is to be produced for every division.

Aron Bock suggested this solution:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>

    <xsl:key match="employee" name="employee-by-refEID" use="@EID"/>

    <xsl:template match="/">
        <xsl:apply-templates select="company/division"/>
    </xsl:template>

    <xsl:template match="division">
        <xsl:text>Division Name = </xsl:text><xsl:value-of select="@DID"/><xsl:text>&#xa;</xsl:text>
        <xsl:apply-templates select="projects/project"/>
    </xsl:template>

    <xsl:template match="project">
        <xsl:text>PID = </xsl:text><xsl:value-of select="@PID"/>
        <xsl:text>, PNAME = </xsl:text><xsl:value-of select="PNAME"/>
        <xsl:text>, BUDGET = </xsl:text><xsl:value-of select="BUDGET"/>
        <xsl:text>&#xa;</xsl:text>

        <!-- list employees using key() -->
        <xsl:apply-templates select="assigns/assign" mode="use-key"/>
        <xsl:text>&#xa;</xsl:text>

        <!-- list employees using relative path -->
        <xsl:apply-templates select="assigns/assign" mode="relative-path"/>

        <xsl:text>&#xa;</xsl:text>
    </xsl:template>

    <xsl:template match="assign" mode="use-key">
        <xsl:text>EID = </xsl:text><xsl:value-of select="@refEID"/>
        <xsl:text>, ENAME = </xsl:text><xsl:value-of select="key('employee-by-refEID', @refEID)/ENAME"/>
        <xsl:text>, OFFICE = </xsl:text><xsl:value-of select="key('employee-by-refEID', @refEID)/OFFICE"/>
        <xsl:text>, HOURS = </xsl:text><xsl:value-of select="HOURS"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>

    <xsl:template match="assign" mode="relative-path">
        <xsl:variable name="refeid" select="@refEID"/>
        <xsl:variable name="emp" select="/company/division/employees/employee[@EID = current()/@refEID]"/>

        <xsl:text>EID = </xsl:text><xsl:value-of select="@refEID"/>
        <xsl:text>, ENAME = </xsl:text><xsl:value-of select="/company/division/employees/employee[@EID = current()/@refEID]/ENAME"/>
        <xsl:text>, OFFICE = </xsl:text><xsl:value-of select="../../../../employees/employee[@EID = @refeid]/OFFICE"/>
        <xsl:text>, BIRTHDATE = </xsl:text><xsl:value-of select="$emp/BIRTHDATE"/>
        <xsl:text>, HOURS = </xsl:text><xsl:value-of select="HOURS"/>
        <xsl:text>&#xa;</xsl:text>
    </xsl:template>
</xsl:stylesheet>

Aron explained:
I assume your real issue was how, once you were at a node, to get related information from another node.  Thus the stylesheet above shows how to do it using keys, using an absolute path and matching on a common attribute, and using a relative path and matching on a common attribute. Along the way it also shows the use of modes, the current() function, and avoiding the current() function by using a previously-initialized variable.

keys() are useful when you need to locate something repeatedly and fast; I believe internally this is represented as a hash structure, so what you gain in speed you may lose in memory.  The usual space/time tradeoff.

When you need to access something repeatedly, but only in one place, it's useful to create a local variable, as we do with $emp.  That gives you to the advantage of doing the full lookup just once and subsequent lookups locally, without the reference staying around until transform-end (as would be the case with keys).

I suggested this stylesheet:

(I am assuming, you wish to generate HTML)

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

<xsl:output method="html" indent="yes" />
 
<xsl:template match="/company">
   <html>
     <head>
       <title/>
     </head>
     <body>
       <xsl:apply-templates select="division" />
     </body>
   </html>
</xsl:template>
 
<xsl:template match="division">
  <table border="1">
    <th>
      <td>
        Division Name = <xsl:value-of select="@DID" />
      </td>
    </th>
    <xsl:for-each select="projects/project">
      <tr>
        <td>PID=<xsl:value-of select="@PID" /></td>
        <td>PNAME=<xsl:value-of select="PNAME" /></td>
        <td>BUDGET=<xsl:value-of select="BUDGET" /></td>
      </tr>
      <xsl:for-each select="assigns/assign">
        <tr>
          <td>EID=<xsl:value-of select="@refEID" /></td>
          <td>ENAME=<xsl:value-of select="ancestor::division[1]/employees/employee[@EID = current()/@refEID]/ENAME" /></td>
          <td>OFFICE=<xsl:value-of select="ancestor::division[1]/employees/employee[@EID = current()/@refEID]/OFFICE" /></td>
          <td>HOURS=<xsl:value-of select="HOURS" /></td>
        </tr>
      </xsl:for-each>
    </xsl:for-each>
  </table>
</xsl:template>
 
</xsl:stylesheet> 

My solution differs from Aron's in writing style. Aron has used xsl:apply-templates (which is push processing style).

While I have used xsl:for-each (which is pull processing style)..

We may even mix these two styles of programming depending on requirement..


20. Transforming XML

The following question was asked on XSL-List.

Given this XML file:

<page>
  <head>
    <title>Example</title>
  </head>
  <content>
    <h1>Example</h1>
    <table>
      <tr>
        <td></td>
        <td>Something #1</td>
      </tr>
      <tr>
        <td>Somthing #2</td>
        <td/>
      </tr>
    </table>
  </content>
</page>

It needs to be transformed to:

<html>
  <head>
    <title>Example</title>
  </head>
  <body>
    <div id="content">
      <h1>Example</h1>
      <table>
        <tr>
          <td>________NOTHING!_______</td>
          <td>Something #1</td>
        </tr>
        <tr>
          <td>Something #2</td>
          <td>________NOTHING!_______</td>
        </tr>
      </table>
    </div>
  </body>
</html>

The stylesheet for this problem is:

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

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

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

<xsl:template match="page">
  <html>
    <xsl:apply-templates />
  </html>
</xsl:template>

<xsl:template match="content">
  <body>
    <div id="content">
      <xsl:apply-templates />
    </div> 
  </body>
</xsl:template>

<xsl:template match="td[normalize-space() = '']">
  <td>__NOTHING__</td>
</xsl:template>

</xsl:stylesheet>


21. Order of xsl:template matching

The following question was asked on XSL-List.

Given this XML chunk:

<TEXT>sldj aldj aldj a</TEXT>
<REPORT>12345</REPORT>

How can I apply templates and have the REPORT appear before the TEXT in the results of the transformation?

I suggested..

For e.g. if the XML is:

<test>
 <TEXT>sldj aldj aldj a</TEXT>
 <REPORT>12345</REPORT>
</test>

You may do:

<xsl:template match="test">
  <xsl:apply-templates select="REPORT" />
  <xsl:apply-templates select="TEXT" />
</xsl:template>

<xsl:template match="TEXT">
  <!-- anything you wish to write -->
</xsl:template>

<xsl:template match="REPORT">
  <!-- anything you wish to write -->
</xsl:template>

David Carlisle explained:

If in the template for the parent you go
<xsl:apply-templates/>
Then nodes will be applied in document order. There are various ways to change that depending what you want to happen in general, eg

<xsl:apply-templates select="REPORT"/>
<xsl:apply-templates select="TEXT"/>

would process REPORT then TEXT and ignore everything else

<xsl:apply-templates select="REPORT"/>
<xsl:apply-templates select="*[not(self::REPORT)]"/>

would process REPORT elements first then any other elements

<xsl:apply-templates>
 <xsl:sort select="name()"/>
</xsl:apply-templates>

would process elements in alphabetic order.

Any of these has the requested effect on your small example of processing REPORT then TEXT.


22. Method for displaying time difference

The following question was asked on XSL-List.

I have two elements:

<due date>2005-04-05</due date>
<actual arrival>2005-04-11T22:21:30</actual arrival>

what is the function to display the day-time difference?

Jeni Tennison provided following reply:

Assuming you're using XSLT 2.0 and that the elements are actually called <due-date> and <actual-arrival>, you can use the minus operator as follows:

xs:dateTime(actual-arrival) - xs:dateTime(xs:date(due-date))

to get the xdt:dayTimeDuration P6DT22H21M30S (6 days, 22 hours, 21 minutes, 30 seconds). You can then use the days-from-duration(), hours-from-duration() etc. functions to extract the values of the individual components from that duration in order to make something readable.

Note that the xs:date() constructor constructs an xs:date from the due date, and the xs:dateTime() constructor casts this to a xs:dateTime by adding 00:00:00 as the time.

Jeni Tennison is an invited expert to XSLT 2.0 WG.
 

23. Transforming a Namespace Declaration

The following question was asked on microsoft.public.xsl Newsgroup.

Given an XML document:

<rootNode>
    <ns1:SomeNode xmlns:ns1="http://www.mydomain.com/SomeNameSpace-A" />
</rootNode>

What is the best way to use XSL to make a document that looks like:

<rootNode>
    <ns1:SomeNode xmlns:ns1="http://www.mydomain.com/SomeNameSpace-B" />
</rootNode>

I suggested this idea:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                      xmlns:ns1="http://whatever"
                      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="node()[local-name() = 'SomeNode']">
  <xsl:copy />  
</xsl:template>
 
</xsl:stylesheet>

Oleg Tkachenko provided following XSLT stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:ns1="http://www.mydomain.com/SomeNameSpace-A">
     <xsl:template match="@*|node()">
         <xsl:copy>
             <xsl:apply-templates select="@*|node()"/>
         </xsl:copy>
     </xsl:template>
     <xsl:template match="ns1:*">
         <xsl:element name="{name()}" namespace="http://www.mydomain.com/SomeNameSpace-B">
             <xsl:apply-templates select="@*|node()"/>
         </xsl:element>
     </xsl:template>
</xsl:stylesheet>
 

24. Getting data between from-date and to-date

The following question was asked on XSL-List.

I have an xml file. It contain number of employee records with basic information like name, age, date of joining etc. I wish to get particular employees covered  between two different dates for example from-date and to-date .

The XML file is:

<Employees>
  <Employee Empname="Raj" DOJ ="02/04/2005" SftTime="0030" />
  <Employee Empname="Rajkumar" DOJ ="02/04/2005" SftTime="0030" />
  <Employee Empname="Raja" DOJ ="03/04/2005" SftTime="0000" />
  <Employee Empname="Ravi" DOJ ="04/04/2005" SftTime="2330" />
  <Employee Empname="john" DOJ ="05/04/2005" SftTime="1600" />
  <Employee Empname="gopi" DOJ ="06/04/2005" SftTime="0100" />
  <Employee Empname="ajith" DOJ ="13/04/2005" SftTime="2200" />
</Employees>

Stylesheet:

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

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

<xsl:variable name="date1" select="'04-03-2005'" />
<xsl:variable name="date2" select="'04-05-2005'" />

<xsl:template match="/Employees">
     <result>
         <xsl:for-each select="Employee">
             <xsl:variable name="doj" select="concat(substring(@DOJ,4,2),'-',substring(@DOJ,1,2),'-',substring(@DOJ,7,4))" />
                 <xsl:if test="($doj &gt;= $date1) and ($doj &lt;= $date2)">
                    <xsl:copy-of select="." />
                 </xsl:if>
        </xsl:for-each>
    </result>
</xsl:template>

</xsl:stylesheet>

(The stylesheet is tested with Saxon 8.4)

Michael Kay provided following explanation:

[1] If the source documents have a schema, and the attributes are defined in the schema as being of type xs:date, and you are using a schema-aware XSLT 2.0 processor, then you can just write

employee[@FromDate le $d and @ToDate ge $d]

If you are not using a schema, but you are using XSLT 2.0, and your dates are stored in the ISO 8601 format that XML Schema uses, then you can still do the same kind of comparison but you need to do a little more work:

employee[xs:date(@FromDate) le $d and xs:date(@ToDate) ge $d]

If you are not using a schema and your dates are in some other format, or if you are using XSLT 1.0, then you will need to do even more work, but this then depends entirely on the format you are starting with.

[2]
> <Employees>
> <Employee Empname="Raj" Empname="Raj" DOJ ="02/04/2005"  
> SftTime="0030"
> >
> <Employee Empname="Rajkumar" DOJ ="02/04/2005"   SftTime="0030"     >
> <Employee Empname="Raja" DOJ ="03/04/2005"   SftTime="0000"     >
> <Employee Empname="Ravi" DOJ ="04/04/2005"   SftTime="2330"     >
> <Employee Empname="john" DOJ ="05/04/2005"   SftTime="1600"     >
> <Employee Empname="gopi" DOJ ="06/04/2005"   SftTime="0100"     >
> <Employee Empname="ajith" DOJ ="13/04/2005"   SftTime="2200"     >
> </Employees>
>
> I want to filter only employees in DOJ between 05/04/2005 and 13/04/2005 these date.

It looks as if your dates are in the format dd/mm/yyyy, which although more logical than the form mm/dd/yyyy, still does not sort naturally.

With XSLT 1.0, the technique is to convert the dates to numbers like this:

number(concat(substring(x, 7, 4), substring(x, 4, 2), substring(x, 1, 2)))

and then compare the numbers.

(This is assuming the month and day are always two digits: I shouldn't really assume that simply because it's true for the six records shown above). 

Rudolf P. Weinmann provided following answer:

As Mukul said, his stylesheet is based on XSLT 2.0 language.
Under XSLT 1.0 the result would be empty, because neither $doj nor $date1 and $date2 can be converted to numbers
for the comparison in the xsl:if statement.

XSLT 1.0 can do the job as well, see example below.

Using param as a toplevel element, you can pass fromDate and toDate from the command line or over an API. I prefer passing them as numbers (see Michael's reasoning) in YYYYMMDD format. When comparing the values with &gt;=, they get implicitly converted to numbers. I prefer the explicit conversion using the number() function.

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

     <xsl:output method="xml" encoding="UTF-8"/>

     <xsl:param name="fromDate" select="'20050403'"/>
     <xsl:param name="toDate" select="'20050405'"/>

     <xsl:template match="Employee">
        <xsl:variable name="doj" select="number(translate('efghcdab','ab/cd/efgh',@DOJ))"/>
        <xsl:if test="$doj &gt;= number($fromDate) and $doj &lt;= number($toDate)">
          <xsl:call-template name="copy"/>
        </xsl:if>
     </xsl:template>

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

     <xsl:template match="@*">
       <xsl:copy/>
     </xsl:template>

</xsl:stylesheet>


25. Getting sibling text

The following question was asked on XSL-List.

I need to isolate text 'Root level text'
 
from xml source:
 
 <p>
   <span>whatever</span>
   Root level text
 </p>

Stylesheet snippet for this problem is:

<xsl:template match="p">
  <xsl:apply-templates select="span" />
</xsl:template>

<xsl:template match="span">
  <xsl:value-of select="normalize-space(following-sibling::text()[1])" />
</xsl:template>


26.  How to Sort?

The following question was asked on XSL-List.

Given this XML document:

<BigList>
  <BigElement>
    <Code>XXP</Code>
    <SmallList>
      <SmallElement>
        <Code>001</Code1>
        <Name>A</Name>
      </SmallElement>
      <SmallElement>
        <Code>002</Code1>
        <Name>X</Name>
      </SmallElement>
    </SmallList>
  </BigElement>
  <BigElement>
    <Code>YYJ</Code>
    <SmallList>
      <SmallElement>
        <Code>01</Code1>
        <Name>B</Name>
      </SmallElement>
      <SmallElement>
        <Code>02</Code1>
        <Name>Z</Name>
      </SmallElement>
    </SmallList>
  </Element>
</BigList>

The desired output after sorting is:

<select id="SmallElements">
  <option value="001">A</option>
  <option value="01">B</option>
  <option value="002">X</option>
  <option value="02">Z</option>
</select>

The stylesheet for this problem is:

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

<xsl:template match="/BigList">
 <select id="SmallElements">
  <xsl:for-each select=".//Name">
    <xsl:sort select="." />
    <option value="{preceding-sibling::Code[1]}"><xsl:value-of select="." /></option>        
  </xsl:for-each>
 </select>
</xsl:template>

</xsl:stylesheet>

Aron Bock suggested:

use xsl:sort

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

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

   <xsl:template match="/">
     <select id="SmallElements">
       <xsl:apply-templates select="BigList/BigElement/SmallList/SmallElement">
         <xsl:sort select="Name"/>
       </xsl:apply-templates>
     </select>
  </xsl:template>

  <xsl:template match="SmallElement">
    <option value="{Code}">
      <xsl:value-of select="Name"/>
    </option>
  </xsl:template>

</xsl:stylesheet>


27. Identity Transformer based file size reducer

The following question was asked on XSL-List.

I need a simple transformation to reduce the size of a file... so like I just need to see the first 10 elements of an xml source which is 10 megs. Those first 10 elements would be the first 10 child elements to the source including their child elements.

Dimitre Novatchev provided following answer:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 
 <xsl:strip-space elements="*"/>
 
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>
 
  <xsl:template match="/*/*[position() > 10]"/>
 
  <xsl:template match="node()[ancestor::*[3]]"/>

</xsl:stylesheet>


28. Sum functionality

Michael Kay told on XSL-List

How to sum over values that are computed from those held in the nodes, rather than the actual string-values of the nodes. The solutions on offer include:

(a) create a temporary tree containing nodes holding the values directly, then sum over the nodes in that temporary tree

(b) a recursive named template

(c) the f:map function in FXSL

(d) the saxon:sum() extension function in Saxon 6.5.3

(e) In XSLT 2.0, sum(for $x in $nodes return number(tokenize($x, ' ')))

(f) In Schema-aware XSLT 2.0, if the type of your gml:Pos nodes is list of numbers, then the sum() function will do the job directly.

Dimitre Novatchev explained:

>  What does map function do?

Formal definition in Haskell prelude (Prelude.hs):

     map              :: (a -> b) -> [a] -> [b]
     map f xs          = [ f x | x <- xs ]


The first line above defines the type (signature) of the "map" function. It takes two arguments:

   -  a function of type a -> b (with domain "a" and codomain "b"  --
      this means that the type of thie arguments is "a" and the type of the results is "b")
      Because "map" has an argument, which is a function, "map" is a higher-order
      function.

   -  a list of elements each of type "a"

The type of the result is a list of elements of type "b".

The second line of the definition above defines exactly the map function. We see that the result of applying a function "map" to its two arguments -- a function "f" and a list "xs" --  is the set of all "f x"  (which means f(x))  where "x" belongs to the list "xs"

More informally, we have a list of elements of the same type ("a") and a function "f", defined on "a" and producing results of type "b".

The result of applying "map" on "f" and a list "xs" (all of whose elements are of type "a") is another list  "ys" , whose elements "y" are the results of applying "f" on the corresponding elements "x" of "xs".


Example:

  map (2 * )  [1,2,3,4]  =  [2,4,6,8]

where (2 *) is a function, which produces twice its argument.

  map string-length ['one', "two", "three", "four"]  =  [3, 3, 5, 4]

then, we'll have:

  sum (map string-length ['one', "two", "three", "four"] ) = 15


> Please explain what does
> sum(f:map(f:string-length(), /*/node())) mean ..

Almost the same as the last line above

29. EXSLT for MSXML4

by Dimitre Novatchev

There is an implementation of EXSLT for MSXML4, which consists of the common:node-set() function and all functions from the "set" module:

     common:node-set()
     set:intersection()
     set:difference()
     set:distinct()
     set:leading()
     set:trailing()
     set:has-same-node()

For more information please see:

    http://www.xml.com/pub/a/2003/08/06/exslt.html


30. XML file (Recursive Transformation)

The following question was asked on XSL-List

It is desired to transform this XML:

<menu name="link1"/>
<menu name="link2">
     <menu name="link2a"/>
     <menu name="link2b"/>
</menu>

to this one:

<ul>
<li>link1</li>
<li>link2
  <ul>
   <li>link2a</li>
   <li>link2b</li>
  </ul>
</li>
</ul>

The stylesheet for this problem is:

<?xml version="1.0"?>
<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="root">
  <ul>
    <xsl:apply-templates />
  </ul> 
</xsl:template>
 
<xsl:template match="menu">
  <xsl:choose>
    <xsl:when test="child::*">
      <li><xsl:value-of select="@name" /></li>
        <ul>
          <xsl:apply-templates />
        </ul> 
      </li>
    </xsl:when>
    <xsl:otherwise>
      <li>
        <xsl:value-of select="@name" />
      </li>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
 
</xsl:stylesheet>

The above stylesheet is applied to XML:

<?xml version="1.0"?>
<root>
<menu name="link1"/>
<menu name="link2">
  <menu name="link2a"/>
  <menu name="link2b"/>
</menu>
</root>

Aron Bock provided this answer:

Luckily, there's an easier and more "natural" way to do this -- don't write  "tags", as you do, but create full-fledged elements.  Using this, with recursion, yields a simple approach.

With this input:

<data>
<menu name="link1"/>
<menu name="link2">
  <menu name="link2a"/>
  <menu name="link2b"/>
</menu>
</data>

This transform, using call-template:

<?xml version="1.0" encoding="iso8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/data">
        <xsl:call-template name="write-menu">
            <xsl:with-param name="items" select="menu"/>
        </xsl:call-template>

    </xsl:template>

    <xsl:template name="write-menu">
        <xsl:param name="items" select="/.."/>
        <ul>
            <xsl:for-each select="$items">
                <li>
                    <xsl:value-of select="@name"/>
                    <xsl:if test="menu">
                        <xsl:call-template name="write-menu">
                            <xsl:with-param name="items" select="menu"/>
                        </xsl:call-template>
                    </xsl:if>
                </li>
            </xsl:for-each>
        </ul>
    </xsl:template>
</xsl:stylesheet>


Produces:

<?xml version="1.0" encoding="UTF-8"?>
<ul>
  <li>link1</li>
  <li>link2<ul>
      <li>link2a</li>
      <li>link2b</li>
    </ul>
  </li>
</ul>

As an aside, if iterating over a set of nodes, last() in a test can identify the last node.


31. Testing if an Element is empty

The following question was asked on XSL-List

How would I test if a node is null?
I have this empty node <EMPTYNODE/>

David Carlisle expressed following opinion:

 How would I test if a node is null?

XPath doesn't have a null value. Testing for empty has an entry in the FAQ.


   like i have this empty node <EMPTYNODE />
   <xsl:when test="EMPTYNODE[.!= '']">

   is not working.

That tests if the string value of EMPTYNODE is not the empty string so <EMPTYNODE /> and <EMPTYNODE><foo/></EMPTYNODE> would test the same way as they both have a string value of ''.

If you want to test if there are no children, then the set of child nodes will be empty
   <xsl:when test="EMPTYNODE[not(node())]">

I expressed following opinion..

I would do <xsl:if test="not(EMPTYNODE/node())">, or as David has written.


32. Traversing nodes and storing a matched value

The following question was asked on XSL-List

Given this XML document:

<menudata>
  <menu name="link1">
    <exsites>0</exsites>
  </menu>
  <menu name="link2">
    <exsites>0</exsites>
     <menu name="link2a">
       <exsites>1</exsites>
       <exsites>2</exsites>
      </menu>
  </menu>
</menudata>

It is desired:

The value of the <exsites> element determines if its parent <menu> element will appear in the output, based on a $siteID parameter set in the stylesheet. In some cases, <menu> has multiple <exsites> children.

I'm not clear how to use XSL to say, "if the value of any <exsites> matches $siteID, then flag its parent's node for different processing".

The stylesheet for this problem is:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:common="http://exslt.org/common"
                version="1.0">
 
 <xsl:output method="text" />
 
 <xsl:param name="siteID" select="'1'" />
 
 <xsl:template match="/">
   <xsl:variable name="rtf">
     <xsl:for-each select="//exsites">
       <xsl:choose>
         <xsl:when test=". = $siteID">
           <menu name="{parent::menu/@name}">
             <process x="yes" />
           </menu>
         </xsl:when>
         <xsl:otherwise>
           <menu name="{parent::menu/@name}">
             <process x="no" />
           </menu>
         </xsl:otherwise>
       </xsl:choose>
     </xsl:for-each>
   </xsl:variable>
  
   <xsl:for-each select="common:node-set($rtf)/menu/process[@x = 'yes']">
     <!-- some desired processing -->
   </xsl:for-each>
 </xsl:template>

</xsl:stylesheet>
 

33. XML to XML Transformation problem

Following question was asked on XSL-List.

It is needed to Transform:

<root>
 <a>
   <b>1</b>
   <c>2</c>
   <d>
      <d1>text1</d1>
      <d2>text2</d2>
   </d>
 </a>
 <a>
   <b>1</b>
   <c>2</c>
   <d>
      <d1>more text1</d1>
      <d2>more text1</d2>
   </d>
 </a>
</root>


to this XML:

<a>
  <b>1</b>
  <c>2</c>
  <d>
     <d1>text1</d1>
     <d2>text2</d2>
  </d>
  <d>
     <d1>more text1</d1>
     <d2>more text1</d2>
  </d>
</a>

The stylesheet for this problem is:

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

<xsl:template match="/root">
  <a>
    <xsl:for-each select="a">
      <xsl:choose>
        <xsl:when test="position() != last()">
          <xsl:copy-of select="*" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy-of select="*[not(self::b)][not(self::c)]" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
  </a>
</xsl:template>
 
</xsl:stylesheet>


34. Generating Sequential IDs

Following question was asked on XSL-List.

I want to generate the sequence ids for all the nodes in the xml tree.

For example my source xml looks like this:

<A>
  <B></B>
  <C>
   <D></D>
  </C>
  <E>
    <F>
     <G></G>
    </F>
  </E>
</A>

I want the output xml like:

<A id=1>
  <B id=2></B>
    <C id=3>
     <D id=4></D>
    </C>
    <E id=5>
      <F id=6>
        <G id=8></G>
      </F>
    </E>
</A>

The following approaches are possible to solve this problem.

Andrew Welch provided the following answer:

<xsl:template match="*">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
      <xsl:attribute name="id">
        <xsl:number level="any" count="*"/>
      </xsl:attribute>
      <xsl:apply-templates select="*"/>
    </xsl:copy>
</xsl:template>

Joris Gillis suggested:

<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:attribute name="id"><xsl:number count="*" level="any"/></xsl:attribute>
    <xsl:apply-templates select="node() | @*" />
   </xsl:copy>
</xsl:template>

</xsl:stylesheet>


35. Eliminating Duplicates with XSLT

XML file is:

<example>
    <employee name="111"/>
    <employee name="222"/>
    <employee name="111"/>
</example>

XSLT stylesheet:

<?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="/example">
    <xsl:for-each select="employee[not(@name = preceding-sibling::employee/@name)]">
        <xsl:value-of select="@name" /><xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
 

36. XML to XML Transformation problem (concatenating attribute values)

Following question was asked on XSL-List.

I want a template which adds the attribute values of its ancestors and create an attribute to the current node. The value to this attribute is the result of concatenation of all the node values added of its parents.

The XML file is something like:

<A seq="1">
  <B seq="2" />
   <C seq="3">
     <D seq="4" />
   </C>
</A>

The stylesheet for this problem is:

<?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() | @*" priority="5">
  <xsl:copy>
    <xsl:apply-templates select="node() | @*" />
  </xsl:copy>
</xsl:template>   
 
<xsl:template match="*" priority="6">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:attribute name="newSeq">
        <xsl:call-template name="concatenate-ancestor-attributes">
           <xsl:with-param name="attr_value" select="''" />
           <xsl:with-param name="element" select="." />
           <xsl:with-param name="ancestors" select="ancestor::*" />
        </xsl:call-template>
      </xsl:attribute>
      <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>
 
<xsl:template name="concatenate-ancestor-attributes">
    <xsl:param name="attr_value" />
    <xsl:param name="element" />
    <xsl:param name="ancestors" />
   
    <xsl:choose>
      <xsl:when test="$element/parent::*">
        <xsl:call-template name="concatenate-ancestor-attributes">
           <xsl:with-param name="attr_value" select="concat($attr_value, $element/parent::*/@seq)" />
           <xsl:with-param name="element" select="$element/parent::*" />
           <xsl:with-param name="ancestors" select="$element/ancestor::*" />
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$attr_value" />
      </xsl:otherwise>
    </xsl:choose>
</xsl:template>
 
</xsl:stylesheet>

David Carlisle provided the following XSLT 2.0 solution:

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

<xsl:template match="*">
  <xsl:copy>
   <xsl:copy-of select="@*"/>
    <xsl:attribute name="newseq" select="string-join(ancestor-or-self::*/(if (@seq) then string(@seq) else '0'),'')"/>
    <xsl:apply-templates/>
   </xsl:copy>
</xsl:template>    

</xsl:stylesheet>
 

37. Flat XML to Hierarchical XML transform

The following question was asked on microsoft.public.xsl Newsgroup.

The xml-document consists of a series of nodes which together form a tree - this is done by a parent/child relationship between the rows.

How do I transform flat xml document to a nested document?

Source document and desired output document are shown below

Source document is:

<Table>
   <Node ID="1" ParentID="0" Text="1" />
   <Node ID="2" ParentID="1" Text="1.1" />
   <Node ID="3" ParentID="2" Text="1.1.1" />
   <Node ID="4" ParentID="2" Text="1.1.2" />
   <Node ID="5" ParentID="2" Text="1.1.3" />
   <Node ID="6" ParentID="1" Text="1.2" />
   <Node ID="7" ParentID="6" Text="1.2.1" />
</Table>

Desired output is:

<?xml version="1.0" encoding="utf-8" ?>
<TREENODES>
<treenode text="1">
  <treenode text="1.1">
    <treenode text="1.1.1" />
    <treenode text="1.1.2" />
    <treenode text="1.1.3" />
  </treenode>
  <treenode text="1.2">
    <treenode text="1.2.1"/>
  </treenode>
</treenode>
</TREENODES>

I want the XSL to run down the tree and create treenode start-tags as long as there is children - and on the way back up the tree the end-tags have to be inserted.

Dimitre Novatchev suggested this stylesheet:

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

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

 <xsl:template match="/">
   <xsl:apply-templates select="*/Node[not(@ParentID = /*/Node/@ID)]"/>
 </xsl:template>

 <xsl:template match="Node">
  <treenode text="{@Text}">
    <xsl:apply-templates select="/*/Node[@ParentID = current()/@ID]">
     </xsl:apply-templates>
  </treenode>
 </xsl:template>
</xsl:stylesheet>
 

38. Attribute to Element Conversion (Transformation problem)

Following question was asked on XSL-List.

Given this XML document:

<Entry>
<Entry name="H" type="System" importance="1">
 <Entry name="I" type="Category" importance="1">
   <Entry name="J" type="Item" importance="1"></Entry>
   <Entry name="K" type="Item" importance="2"></Entry>
 </Entry>
 <Entry name="L" type="Category" importance="2">
   <Entry name="N" type="Item" importance="1"></Entry>
   <Entry name="M" type="Item" importance="2"></Entry>
 </Entry>
</Entry>
<Entry name="A" type="System" importance="2">
 <Entry name="E" type="Category" importance="1">
   <Entry name="G" type="Item" importance="1"></Entry>
   <Entry name="F" type="Item" importance="2"></Entry>
 </Entry>
 <Entry name="B" type="Category" importance="2">
   <Entry name="C" type="Item" importance="1"></Entry>
   <Entry name="D" type="Item" importance="2"></Entry>
 </Entry>
</Entry>
</Entry>

It is required to reform it by "type" with some new elements created so that the output XML will look like:

<Entry>
<System type="System" importance="1"><name>H</name>
 <Category type="Category" importance="1"><name>I</name>
   <Item type="Item" importance="1"><name>J</name></Item>
   <Item type="Item" importance="2"><name>K</name></Item>
 </Category>
 <Category type="Category" importance="2"><name>L</name>
   <Item type="Item" importance="1"><name>N</name></Item>
   <Item type="Item" importance="2"><name>M</name></Item>
 </Category>
</System>
<System type="System" importance="2"><name>A</name>
 <Category type="Category" importance="1"><name>E</name>
   <Item type="Item" importance="1"><name>G</name></Item>
   <Item type="Item" importance="2"><name>F</name></Item>
 </Category>
 <Category type="Category" importance="2"><name>B</name>
   <Item type="Item" importance="1"><name>C</name></Entry>
   <Item type="Item" importance="2"><name>D</name></Entry>
 </Category>
</System>
</Entry>

The stylesheet for this problem is:

<?xml version="1.0"?>
<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="Entry[parent::*]">
  <xsl:element name="{@type}">
    <xsl:attribute name="type"><xsl:value-of select="@type" /></xsl:attribute>
    <xsl:attribute name="importance"><xsl:value-of select="@importance" /></xsl:attribute>
    <name><xsl:value-of select="@name" /></name>
    <xsl:apply-templates />
  </xsl:element>
</xsl:template>

</xsl:stylesheet>
 

39. Generating Serial Numbers

Following question was asked on XSL-List.

I want to count all preceding sibling (steps) but they could be in different elements.

<element1>
 <element2>
    <step/>
    <step/>
 </element2>
 <element3>
    <step/>
 </element3>
 <element4>
    <step/>
    <step/>
    <step/>
 </element4>
</element1>

The output I am after is:

<element1>
 <element2>
    <step number="1" />
    <step number="2"/>
 </element2>
 <element3>
    <step number="3"/>
 </element3>
 <element4>
    <step number="4"/>
    <step number="5"/>
    <step number="6"/>
 </element4>
</element1>

The stylesheet for this problem is:

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

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

<xsl:template match="/element1">
  <element1>
    <xsl:for-each select="*">
      <xsl:copy>
        <xsl:call-template name="numberingTemplate">
          <xsl:with-param name="node-set" select="*" />
        </xsl:call-template>
      </xsl:copy>
    </xsl:for-each>
  </element1>
</xsl:template>

<xsl:template name="numberingTemplate">
  <xsl:param name="node-set" />

  <xsl:for-each select="$node-set">
    <xsl:element name="{name()}">
      <xsl:attribute name="number"><xsl:value-of select="count(self::* | preceding::step)" /></xsl:attribute>
    </xsl:element>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
 

40. Displaying time in UTC in a XSLT stylesheet

Following question was asked on XSL-List.

Stylesheet (written using XSLT 2.0):

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

<xsl:output method="text" />

<xsl:template match="/">
        UTC time - <xsl:value-of select="xyz:UtcTime(xs:string(current-time()))" />
</xsl:template>

<xsl:function name="xyz:UtcTime">
<xsl:param name="curr-time" as="xs:string"/>

<xsl:choose>
        <xsl:when test="contains($curr-time,'+')">
            <xsl:variable name="local-time" select="substring-before($curr-time,'+')" />
            <xsl:variable name="time-zone" select="substring-after($curr-time,'+')" />
            <xsl:variable name="x" select="substring-before($local-time,':')" />
            <xsl:variable name="y" select="substring-before(substring-after($local-time,':'),':')" />
            <xsl:variable name="z" select="substring-after(substring-after($local-time,':'),':')" />
            <xsl:variable name="sec1" select="(xs:integer($x) * 60 * 60) + (xs:integer($y) * 60) + xs:float($z)" />
            <xsl:variable name="u" select="substring-before($time-zone,':')" />
            <xsl:variable name="v" select="substring-after($time-zone,':')" />
            <xsl:variable name="sec2" select="(xs:integer($u) * 60 * 60) + (xs:integer($v) * 60)" />
            <xsl:variable name="sec3" select="$sec1 - $sec2" />
            <xsl:value-of select="floor(($sec3 div 60) div 60)" />:<xsl:value-of select="floor(($sec3 div 60) mod 60)" />:<xsl:value-of select="$sec3 - ((floor(($sec3 div 60) div 60) * 60 * 60) + (floor(($sec3 div 60) mod 60) * 60))" />
        </xsl:when>
        <xsl:when test="contains($curr-time,'-')">
            <xsl:variable name="local-time" select="substring-before($curr-time,'-')" />
            <xsl:variable name="time-zone" select="substring-after($curr-time,'-')" />
            <xsl:variable name="x" select="substring-before($local-time,':')" />
            <xsl:variable name="y" select="substring-before(substring-after($local-time,':'),':')" />
            <xsl:variable name="z" select="substring-after(substring-after($local-time,':'),':')" />
            <xsl:variable name="sec1" select="(xs:integer($x) * 60 * 60) + (xs:integer($y) * 60) + xs:float($z)" />
            <xsl:variable name="u" select="substring-before($time-zone,':')" />
            <xsl:variable name="v" select="substring-after($time-zone,':')" />
            <xsl:variable name="sec2" select="(xs:integer($u) * 60 * 60) + (xs:integer($v) * 60)" />
            <xsl:variable name="sec3" select="$sec1 - $sec2" />
            <xsl:value-of select="floor(($sec3 div 60) div 60)" />:<xsl:value-of select="floor(($sec3 div 60) mod 60)" />:<xsl:value-of select="$sec3 - ((floor(($sec3 div 60) div 60) * 60 * 60) + (floor(($sec3 div 60) mod 60) * 60))" />
        </xsl:when>
</xsl:choose>
</xsl:function>

</xsl:stylesheet>

David Carlisle suggested:

Use:

adjust-time-to-timezone(current-time(),xdt:dayTimeDuration('PT0H'))
where xdt is xmlns:xdt="http://www.w3.org/2005/04/xpath-datatypes"
 

41. Identity Transform (case conversion)

What will be the XSLT stylesheet, which when given any XML document as input, will produce an identical XML document as output. But the condition is: all the letters (a-z) anywhere in source XML should change from small case to upper case.

The stylesheet for this problem is:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" exclude-result-prefixes="exslt">

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

<xsl:variable name="small" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="caps" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />

<xsl:template match="*">
    <xsl:element name="{translate(name(), $small, $caps)}">
        <xsl:variable name="nsHolder">
            <test>
                <xsl:for-each select="namespace::*[not(. = 'http://www.w3.org/XML/1998/namespace')]">
                    <xsl:attribute name="{translate(name(), $small, $caps)}:dummy{position()}" namespace="{translate(., $small, $caps)}"></xsl:attribute>
                </xsl:for-each>
            </test>
        </xsl:variable>
        <xsl:copy-of select="exslt:node-set($nsHolder)/test/namespace::*"/>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates/>
    </xsl:element>
</xsl:template>

<xsl:template match="text()">
    <xsl:value-of select="translate(., $small, $caps)" />
</xsl:template>

<xsl:template match="@*">
    <xsl:attribute name="{translate(name(), $small, $caps)}" namespace="{translate(namespace-uri(), $small, $caps)}"><xsl:value-of select="translate(., $small, $caps)" /></xsl:attribute>
</xsl:template>

<xsl:template match="processing-instruction()">
    <xsl:processing-instruction name="{translate(name(), $small, $caps)}"><xsl:value-of select="translate(., $small, $caps)" /></xsl:processing-instruction>
</xsl:template>

<xsl:template match="comment()">
    <xsl:comment><xsl:value-of select="translate(., $small, $caps)" /></xsl:comment>
</xsl:template>

</xsl:stylesheet>

Thanks to George Cristian Bina, Michael Kay and Dimitre Novatchev for suggestions.
 

42. XML to XML transformation (Structural changes)

Following question was asked on XSL-List.

I'm trying to copy a single element ("topic") and attribute ("id") to a new XML file, discarding all other elements and attributes. I'd also like to rename the topic element as topicref, and rename the id attribute as href.

Input XML:

<topic id="unique_id">
     <title>Title</title>
     <body>
        <p>Some text.</p>
    </body>
    <topic id="unique_id">
        <title>Title</title>
        <body>
           <p>Some text.</p>
        </body>
   </topic>
   <topic id="unique_id">
       <title>Title</title>
       <body>
           <p>Some text.</p>
       </body>
       <topic id="unique_id">
          <title>Title</title>
          <body>
            <p>Some text.</p>
          </body>
       </topic>
   </topic>
</topic>

Desired output is:

<topicref href="unique_id">
       <topicref href="unique_id"/>
       <topicref href="unique_id">
            <topicref href="unique_id"/>
       </topicref>
</topicref>

The stylesheet for this problem is:

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

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

  <xsl:template match="topic">
    <topicref href="{@id}">
      <xsl:apply-templates />
    </topicref>
  </xsl:template>

  <xsl:template match="text()" />

</xsl:stylesheet>
 

43. Writing modular stylesheet (XSLT 2.0)

Following question was asked on XSL-List.

I'm trying to find a better alternative to our current test XSL template. Currently, we have the following XML file structure (extremely simplified from the original):

<resultset>
  <term>
    <termname>First Term</termname>
    <termattrs>
      <termdef>This is the firstterm description.</termdef>
    </termattrs>
  </term>
  <term>
    <termname>Second Term</termname>
    <termattrs>
      <termdef>This is the second term description.</termdef>
    </termattrs>
  </term>
</resultset>

We're trying build a glossary of the terms listed in alphabetical order. In other words, after the translation, viewing the XML (in IE) should produce an output similar to the following:

A
B
...
F
First Term (This is the first term description.)
...
S
Second Term (This is the second term description.)
...
Z

Unfortunately, our current XSL logic involves an <xsl:for-each> loop which tests whether the first letter of <termname> begins with a letter. However, this is done for *each* letter, so we have duplicate code,
i.e.:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
...
<xsl:template match="/resultset">
  <p>A</p>
  <xsl:for-each select="term">
    <xsl:if test="starts-with(termname, 'A') or starts-with(termname, 'a')">
      <xsl:value-of select="termname" />
      <xsl:value-of select="termattrs/termdef" />
    </xsl:if>
  </xsl:for-each>

  <p>B</p>
  <xsl:for-each select="term">
    <xsl:if test="starts-with(termname, 'B') or starts-with(termname, 'b')">
       <xsl:value-of select="termname" />
       <xsl:value-of select="termattrs/termdef" />
    </xsl:if>
  </xsl:for-each>
...
</xsl:template>
</xsl:stylesheet>

Is there a better method than using multiple <xsl:for-each> loops?

Here is a better technique:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                      xmlns:xs="http://www.w3.org/2001/XMLSchema"
                      xmlns:my="http://dummy"
                      version="2.0">

<xsl:output method="html"/>

<xsl:template match="/resultset">
    <xsl:variable name="here" select="." />
    <html>
      <head>
        <title/>
      </head>
      <body>
         <xsl:for-each select="65 to 90">
            <xsl:variable name="letter" select="codepoints-to-string(.)"/>
            <p><xsl:value-of select="$letter" /></p>
            <xsl:variable name="htmlFragment" select="my:getHtmlFragment($letter, $here)" />
            <xsl:copy-of select="$htmlFragment" />
         </xsl:for-each>
      </body>
    </html>
</xsl:template>

<xsl:function name="my:getHtmlFragment" as="node()*">
  <xsl:param name="letter" as="xs:string"/>
  <xsl:param name="here" as="element()"/>

  <ul>
    <xsl:for-each select="$here/term[starts-with(termname, $letter) or starts-with(termname, lower-case($letter))]">
       <li><xsl:value-of select="termname" /> (<xsl:value-of select="termattrs/termdef" />)</li>
    </xsl:for-each>
  </ul>
</xsl:function>

</xsl:stylesheet>

(Thanks to David Carlisle for suggestions)
 

44. Creating a two-column HTML table from an unsorted list of XML nodes

The following problem was posted on the newsgroup, XSLT@googlegroups.com

I have some XML elements that are not sorted:

<element name="sam" />
<element name="bob" />
<element name="alice" />
<element name="mary" />
<element name="fred" />
<element name="barbara" />
etc.

and we want to create a 2-column sorted HTML table with the names,
thus if we had the 6 elements above

alice    fred
barbara    mary
bob    sam

We can have any number of elements. We need a XSLT 1.0 solution.

Solution:

At first thought, I felt this could be a difficult problem to solve, because the sorted output is split into two columns (and in a spiral fashion - i.e, down and then up).

After thinking a bit, I came up with the following solution, which uses the node-set extension function and some minor arithmetic trick:

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

<xsl:output method="html" />

<xsl:template match="/elements">
    <html>
        <head>
            <title/>
        </head>
        <body>
            <xsl:variable name="n" select="count(element)" />
            <xsl:variable name="no-of-rows">
                <xsl:choose>
                    <xsl:when test="$n mod 2 = 1">
                        <xsl:value-of select="floor(($n div 2) + 1)" />
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$n div 2" />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:variable>
            <table>
                <xsl:variable name="sorted-sequence">
                    <xsl:apply-templates select="element">
                        <xsl:sort select="@name" />
                    </xsl:apply-templates>
                </xsl:variable>
                <xsl:for-each select="exslt:node-set($sorted-sequence)/name[position() &lt;= $no-of-rows]">
                    <xsl:variable name="x" select="position() + $no-of-rows" />
                    <tr>
                        <td>
                            <xsl:value-of select="." />
                        </td>
                        <td>
                            <xsl:value-of select="exslt:node-set($sorted-sequence)/name[$x]" />
                        </td>
                    </tr>
                </xsl:for-each>
            </table>
        </body>
    </html>
</xsl:template>

<xsl:template match="element">
    <name>
        <xsl:value-of select="@name" />
    </name>
</xsl:template>

</xsl:stylesheet>

I tried the above stylesheet with the following XML:

<elements>
    <element name="sam" />
    <element name="bob" />
    <element name="alice" />
    <element name="mary" />
    <element name="fred" />
    <element name="barbara" />
</elements>