Search code examples
xmlxsltxpathscopetransformation

XSLT changing a node via a named template call from within a different node


Edit: the error was in a wrong when condition.

Source xml:

<?xml version="1.0" encoding="UTF-8"?>
<mainNode>
   <levelOneNodes>
      <levelOneNode>
         <value name="triggeringValue">
            <levelTwoNodes>
               <levelTwoNode attributeOne="A" attributeTwo="A" attributeThree="A">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="B1" attributeTwo="B1" attributeThree="B1">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="C1" attributeTwo="C1" attributeThree="C1">
                  <textData>This is some text.</textData>
               </levelTwoNode>
            </levelTwoNodes>
         </value>
      </levelOneNode>
      <levelOneNode>
         <value name="anotherValue">
            <levelTwoNodes>
               <levelTwoNode attributeOne="A" attributeTwo="A" attributeThree="A">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="B2" attributeTwo="B2" attributeThree="B2">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="C2" attributeTwo="C2" attributeThree="C2">
                  <textData>This is some text.</textData>
               </levelTwoNode>
            </levelTwoNodes>
         </value>
      </levelOneNode>
    </levelOneNodes>
</mainNode>

Expected trasnformed xml:

<?xml version="1.0" encoding="UTF-8"?>
<mainNode>
   <levelOneNodes>
      <levelOneNode>
         <value name="triggeringValue">
            <levelTwoNodes>
               <levelTwoNode injectedAttribute="triggeringValue" attributeOne="A" attributeTwo="A" attributeThree="A">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode injectedAttribute="triggeringValue" attributeOne="B1" attributeTwo="B1" attributeThree="B1">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode injectedAttribute="triggeringValue" attributeOne="C1" attributeTwo="C1" attributeThree="C1">
                  <textData>This is some text.</textData>
               </levelTwoNode>
            </levelTwoNodes>
         </value>
      </levelOneNode>
      <levelOneNode>
         <value name="anotherValue">
            <levelTwoNodes>
               <levelTwoNode injectedAttribute="triggeringValue" attributeOne="A" attributeTwo="A" attributeThree="A">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="B2" attributeTwo="B2" attributeThree="B2">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="C2" attributeTwo="C2" attributeThree="C2">
                  <textData>This is some text.</textData>
               </levelTwoNode>
            </levelTwoNodes>
         </value>
      </levelOneNode>
    </levelOneNodes>
</mainNode>

That is, when a levelTwoNode node nested into a value node with attribute name "triggeringValue" is encountered, all the levelTwoNode nodes in the document with the same attributeOne and attributeTwo as the triggering node's should be injected with the attribute injectedAttribute="triggeringValue".

I am using this xslt:

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

    <xsl:output encoding="Windows-1252" method="xml" indent="no" omit-xml-declaration="no" />

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

    <xsl:template match="//levelOneNodes/levelOneNode/value/levelTwoNodes/levelTwoNode" >
    
        <xsl:call-template name="myTemplate">
        
            <xsl:with-param name="myAttrOne" select=" current()/@attributeOne " />
            <xsl:with-param name="myAttrTwo" select=" current()/@attributeTwo " />
            <xsl:with-param name="myValue" select=" current()/../../@name " />

        </xsl:call-template>

    </xsl:template>
    
    <xsl:template name="myTemplate">    

        <xsl:param name="myAttrOne" />
        <xsl:param name="myAttrTwo" />
        <xsl:param name="myValue" />

        <xsl:choose>

            <xsl:when test="boolean(//levelTwoNode[(@attributeOne = $myAttrOne) and (@attributeTwo = $myAttrTwo)]) and boolean($myValue = 'triggeringValue')">
            
                <xsl:copy>
                
                    <xsl:attribute name="injectedAttribute"><xsl:value-of select="$myValue"/></xsl:attribute>
                    <xsl:apply-templates select="@* |node() "/>

                </xsl:copy>

            </xsl:when>

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

                </xsl:copy>

            </xsl:otherwise>
        </xsl:choose>

    </xsl:template> 

</xsl:stylesheet>

that results in this xml:

<?xml version="1.0" encoding="Windows-1252"?><mainNode>
   <levelOneNodes>
      <levelOneNode>
         <value name="triggeringValue">
            <levelTwoNodes>
               <levelTwoNode injectedAttribute="triggeringValue" attributeOne="A" attributeTwo="A" attributeThree="A">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode injectedAttribute="triggeringValue" attributeOne="B1" attributeTwo="B1" attributeThree="B1">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode injectedAttribute="triggeringValue" attributeOne="C1" attributeTwo="C1" attributeThree="C1">
                  <textData>This is some text.</textData>
               </levelTwoNode>
            </levelTwoNodes>
         </value>
      </levelOneNode>
      <levelOneNode>
         <value name="anotherValue">
            <levelTwoNodes>
               <levelTwoNode attributeOne="A" attributeTwo="A" attributeThree="A">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="B2" attributeTwo="B2" attributeThree="B2">
                  <textData>This is some text.</textData>
               </levelTwoNode>
               <levelTwoNode attributeOne="C2" attributeTwo="C2" attributeThree="C2">
                  <textData>This is some text.</textData>
               </levelTwoNode>
            </levelTwoNodes>
         </value>
      </levelOneNode>
    </levelOneNodes>
</mainNode>

where the new attribute injectedAttribute is not injected into the first levelTwoNode of the second levelOneNode (the one with the child node value with attribute name "anotherValue") even though it has the same attributeOne and attributeTwo (with value "A") of one of the levelTwoNode nodes inside the first levelOneNode node (the one with the child node value with attribute name "triggeringValue"). The attribute injectedAttribute is injected only in all the levelTwoNode nodes inside the triggeringValue value node.

Why does this condition:

<xsl:when test="boolean(//levelTwoNode[(@attributeOne = $myAttrOne) and (@attributeTwo = $myAttrTwo)]) and boolean($myValue = 'triggeringValue')">

is limited in the scope of the calling node? The query //levelTwoNode[(@attributeOne = $myAttrOne) and (@attributeTwo = $myAttrTwo)] should fetch all levelTwoNode nodes with the passed attributes and the condition $myValue = 'triggeringValue' should not limit that query.

What am I missing?

Thanks for any help.


Solution

  • You say "the condition $myValue = 'triggeringValue' should not limit that query" but it should. The variable $myValue is bound to current()/../../@name and I think you will find its value is "anotherValue".