xsl:apply-templates not iterating over each node given by select

I'm using Saxon HE 9-4-0-3J. My intent is to use the select attribute on xsl:apply-templates to iterate over each <RECORD> element, but only a single <RECORD> element in the xml is being processed.

xml

<ENVELOPE>
<PRODUCT>
    <HEAD><FLAG>No</FLAG></HEAD>
    <BODY>
        <RECORD><Value>9</Value></RECORD>
        <RECORD><Value>10</Value></RECORD>
        <MISC>9</MISC>
    </BODY>
</PRODUCT>

xslt 2.0

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

<xsl:template match="/ENVELOPE">
<Interface>
   <Data><xsl:apply-templates select="PRODUCT"/></Data>
</Interface>
</xsl:template>

<xsl:template match="PRODUCT">
   <xsl:choose>
      <xsl:when test="HEAD/FLAG='Yes'">
         <xsl:attribute name="Match">Affirmative</xsl:attribute>
         <xsl:apply-templates select="BODY/RECORD" mode="affr"/>
      </xsl:when>
      <xsl:when test="HEAD/FLAG='No'">
         <xsl:attribute name="Match">Negative</xsl:attribute>
         <xsl:apply-templates select="BODY/RECORD" mode="neg"/>
      </xsl:when>
   </xsl:choose>
</xsl:template>

<xsl:template match="RECORD" mode="affr">
   <xsl:attribute name="Value"><xsl:value-of select="Value"/></xsl:attribute>
</xsl:template>

<xsl:template match="RECORD" mode="neg">
   <xsl:attribute name="Value"><xsl:value-of select="Value"/></xsl:attribute>
</xsl:template>

</xsl:stylesheet>

Output

<Interface>
      <Data Match="Negative" Value="9"/> <!-- this line doesn't appear, I want it to -->
      <Data Match="Negative" Value="10"/>
</Interface>

Answers


The <DATA...> output tag is generated when you match <ENVELOPE...>, so there can only be one of these output. Any <xsl:attribute> directives merely add to this output tag, with later values overwriting earlier ones.

You must explicitly generate the DATA tags you want output, as in:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/ENVELOPE">
    <Interface>
      <xsl:apply-templates select="PRODUCT"/>
    </Interface>
  </xsl:template>

  <xsl:template match="PRODUCT">
    <xsl:variable name="flag">
      <xsl:choose>
        <xsl:when test="HEAD/FLAG='Yes'">Affirmative</xsl:when>
        <xsl:when test="HEAD/FLAG='No'">Negative</xsl:when>
      </xsl:choose>
    </xsl:variable>

    <xsl:apply-templates select="BODY/RECORD">
      <xsl:with-param name="flag"><xsl:value-of select="$flag"/></xsl:with-param>
    </xsl:apply-templates>

  </xsl:template>

  <xsl:template match="RECORD">
    <xsl:param name="flag"/>
    <Data Value="{Value}" Match="{$flag}"/>
  </xsl:template>

</xsl:stylesheet>

The problem is that you are creating two attributes with the same name -- Value. In a wellformed XML document an element cannot have more than one attribute with the same name.

To quote the W3C XSLT 2.0 Specification (this is exactly the same in XSLT 1.0, too):

9. If an attribute A in the result sequence has the same name as another attribute B that appears later in the result sequence, then attribute A is discarded from the result sequence.

So, whenever XSLT generates for an element more than one attributes with the same name, only the last attribute is copied to the output -- and this is what you see.

Here is a short and simple solution (only two templates, no conditionals, no parameters):

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vDoc" select="/"/>

 <xsl:variable name="vMatch" select=
 "('Affirmative', 'Negative')[2 - number($vDoc/*/*/HEAD/FLAG eq 'Yes')]"/>

 <xsl:template match="/*">
  <Interface>
    <xsl:apply-templates select="*/BODY/RECORD"/>
  </Interface>
 </xsl:template>

 <xsl:template match="RECORD">
  <Data Match="{$vMatch}" Value="{Value}"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<ENVELOPE>
    <PRODUCT>
        <HEAD>
            <FLAG>No</FLAG>
        </HEAD>
        <BODY>
            <RECORD>
                <Value>9</Value>
            </RECORD>
            <RECORD>
                <Value>10</Value>
            </RECORD>
            <MISC>9</MISC>
        </BODY>
    </PRODUCT>
</ENVELOPE>

the wanted, correct result is produced:

<Interface>
   <Data Match="Negative" Value="9"/>
   <Data Match="Negative" Value="10"/>
</Interface>

Need Your Help

Append rows between rows in a table

javascript jquery

I have a table in the following structure, there are rows that i would like to append to the table dynamically.

Loop trought Content Controls Checkbox inside different tables

vba ms-word word-vba

I have document with multiple tables in it(as seen in the attached image), so I try to verify only the field with the status in the column "complete", those that are not selected (False)to work in...

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.