1

I am new to XSL and am trying to create a text csv header row with the code below. All I get is the content of each StartTime tag. What am I missing?

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

    <xsl:variable name="headers">
        <xsl:call-template name="do_header">
            <xsl:with-param name="headers" select="//Event//@channel[not(.=../../preceding-sibling::Event//@channel)]" />
        </xsl:call-template>
    </xsl:variable>
<!-- Write the Header -->

    <xsl:template name="do_header">
        <xsl:param name="headers" />
        <xsl:text>Time,</xsl:text>
        <xsl:for-each select="$headers">
            <xsl:value-of select="."/>
            <xsl:text>,</xsl:text>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Example XML:

<?xml version="1.0" encoding="UTF-8"?>
<Program>
   <Event>
      <StartTime units="us">5</StartTime>
      <LaserOn channel="HeNe Laser" />
   </Event>
   <Event>
      <StartTime units="us">15</StartTime>
      <LaserOff channel="HeNe Laser" />
   </Event>
   <Event>
      <StartTime units="us">55</StartTime>
      <LaserOn channel="HeNe Laser" />
      <LaserOn channel="TiSaph" />
   </Event>
   <Event>
      <StartTime units="us">125</StartTime>
      <LaserOff channel="TiSaph" />
   </Event>
   <Event>
      <StartTime units="us">905</StartTime>
      <LaserOn channel="HeNe Laser" />
      <LaserOn channel="TiSaph" />
   </Event>
   <Event>
      <StartTime units="us">1500</StartTime>
      <LaserOff channel="HeNe Laser" />
   </Event>
</Program>

Output of the xslt. It only output the content of the StartTime tags ie. 5 15 55 125 905 1500

Mads Hansen
  • 63,927
  • 12
  • 112
  • 147
  • In addition to the answer provided below by Mads Hansen, do note that this is not a good method to generate unique values - read here why: http://www.jenitennison.com/xslt/grouping/muenchian.html – michael.hor257k Feb 11 '22 at 06:39

1 Answers1

1

You have created a variable $header and a named template that is used to generate headers, but you haven't created any other templates that would match any content and use the $header.

Therefore, the default processing occurs, which is the equivalent of matching on elements and applying templates to children nodes, and a template matching text() that returns it. So, if you apply an XSLT without matching any templates that would do something different, you just generate output with all of the text().

You would generate the same output if your stylesheet were just:

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

So, in order to generate your header, you could create a template that matches on the root node / and then return the value of the $header variable, which has the headers produced by calling that do_header named template:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:output method="text" omit-xml-declaration="yes" encoding="UTF-8" indent="no" />
    
    <xsl:variable name="headers">
        <xsl:call-template name="do_header">
            <xsl:with-param name="headers" select="//Event//@channel[not(.=../../preceding-sibling::Event//@channel)]" />
        </xsl:call-template>
    </xsl:variable>
    
    <!-- Write the Header -->
    <xsl:template match="/">
        <xsl:value-of select="$headers"/>
    </xsl:template>
    
    <xsl:template name="do_header">
        <xsl:param name="headers" />
        <xsl:text>Time,</xsl:text>
        <xsl:for-each select="$headers">
            <xsl:value-of select="."/>
            <xsl:text>,</xsl:text>
        </xsl:for-each>
    </xsl:template>
    
</xsl:stylesheet>

You could also just call that do_header template and avoid creating the variable:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:output method="text" omit-xml-declaration="yes" encoding="UTF-8" indent="no" />
        
    <!-- Write the Header -->
    <xsl:template match="/">
        <xsl:call-template name="do_header">
            <xsl:with-param name="headers" select="//Event//@channel[not(.=../../preceding-sibling::Event//@channel)]" />
        </xsl:call-template>
    </xsl:template>
    
    <xsl:template name="do_header">
        <xsl:param name="headers" />
        <xsl:text>Time,</xsl:text>
        <xsl:for-each select="$headers">
            <xsl:value-of select="."/>
            <xsl:text>,</xsl:text>
        </xsl:for-each>
    </xsl:template>
    
</xsl:stylesheet>
Mads Hansen
  • 63,927
  • 12
  • 112
  • 147