Home > other >  XSLT changing a node via a named template call from within a different node
XSLT changing a node via a named template call from within a different node

Time:02-12

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.

CodePudding user response:

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".

CodePudding user response:

This is very confusing. I could not understand your code at all. If I understand your description correctly (which is a VERY big if), then you want to do something like:

XSLT 1.0

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

<xsl:key name="trigger" match="value[@name='triggeringValue']/levelTwoNodes/levelTwoNode" use="concat(@attributeOne, '|', @attributeTwo)" />

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

<xsl:template match="levelTwoNode[key('trigger', concat(@attributeOne, '|', @attributeTwo))]">
    <xsl:copy>
        <xsl:attribute name="injectedAttribute">triggeringValue</xsl:attribute>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

The way this works is it indexes all levelTwoNode elements that are descendants of <value name="triggeringValue">. Then, any levelTwoNode element that has the same two attribute values as one of the indexed elements (incl. the indexed elements themselves) is modified by adding the injectedAttribute attribute.

  • Related