How to apply multiple template definitions where each of them is changing the same XML structure

12,206

The second template match="ROOT/E/OTHER/DEAL/KEYS" that should satisfy the second challenge part: to Add the second KEY node to the /ROOT/E/OTHER/DEAL/KEYS structure, has been ignored completely.

The problem is that in the template matching ROOT there isn't any xsl:apply-templates. A template only gets selected for execution in result of an xsl:apply-templates instruction. In your code there isn't any xsl:apply-templates, therefore the XSLT processor doesn't apply templates any more and finishes execution.

It would be different if you replaced xsl:copy-of with xsl:apply-templates.

Here is how to do this:

<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="/*">
  <xsl:copy>
    <xsl:apply-templates select="@*|B/preceding-sibling::node()"/>
    <xsl:apply-templates select="B"/>
    <B1>some B1 text</B1>
    <xsl:apply-templates select="B/following-sibling::node()"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="KEY">
  <xsl:copy>
   <xsl:apply-templates select="@*|node()"/>
   <KEY>
    <KeyIdentifierType>KeyIdentifierTypeB</KeyIdentifierType>
    <KeyValue>
     <xsl:value-of select="'654321|1'"/>
    </KeyValue>
   </KEY>
   </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<ROOT>
    <A>some A text</A>
    <B>some B text</B>
    <C>some C text</C>
    <D>some D text</D>
    <E>
        <OTHER>
            <DEAL>
                <KEYS>
                    <KEY>
                        <KeyIdentifierType>KeyIdentifierTypeA</KeyIdentifierType>
                        <KeyValue>123456|1</KeyValue>
                    </KEY>
                </KEYS>
            </DEAL>
        </OTHER>
    </E>
</ROOT>

the wanted, correct result is produced:

<ROOT>
   <A>some A text</A>
   <B>some B text</B>
   <B1>some B1 text</B1>
   <C>some C text</C>
   <D>some D text</D>
   <E>
      <OTHER>
         <DEAL>
            <KEYS>
               <KEY>
                  <KeyIdentifierType>KeyIdentifierTypeA</KeyIdentifierType>
                  <KeyValue>123456|1</KeyValue>
                  <KEY>
                     <KeyIdentifierType>KeyIdentifierTypeB</KeyIdentifierType>
                     <KeyValue>654321|1</KeyValue>
                  </KEY>
               </KEY>
            </KEYS>
         </DEAL>
      </OTHER>
   </E>
</ROOT>
Share:
12,206
Haris Priganica
Author by

Haris Priganica

Updated on June 19, 2022

Comments

  • Haris Priganica
    Haris Priganica almost 2 years

    In this code example I have 2 challenges one is to set B1 node after B node and before node C, D and E and the second challenge is to Add the second KEY node to the /ROOT/E/OTHER/DEAL/KEYS structure.

    This XML sample:

            <ROOT>
              <A>some A text</A>
              <B>some B text</B>
              <C>some C text</C>
              <D>some D text</D>
              <E>
                <OTHER>
                  <DEAL>
                    <KEYS>
                      <KEY>
                        <KeyIdentifierType>KeyIdentifierTypeA</KeyIdentifierType>
                        <KeyValue>123456|1</KeyValue>
                      </KEY>
                    </KEYS>
                  </DEAL>
                </OTHER>
              </E>
            </ROOT>
    

    after the transformation:

        <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <xsl:strip-space elements="*"/>
            <xsl:output method="xml" indent="yes"/>
    
            <xsl:template match="@*|node()">
                <xsl:copy>
                    <xsl:apply-templates  select="@*|node()"/>
                </xsl:copy>
            </xsl:template>
    
            <!-- Identifiers are added by the system. Need to pass parms from the calling program -->
            <xsl:template match="ROOT" name="add-B1">
                <xsl:variable name="elements-after" select="C|D|E"/>
                <xsl:copy>
                    <xsl:copy-of select="* except $elements-after"/>
                    <B1>some B1 text</B1>
                    <xsl:copy-of select="$elements-after"/>
                </xsl:copy>
            </xsl:template>
    
            <!-- KEY is added by the system. Need to pass parms from the calling program -->
            <xsl:template match="ROOT/E/OTHER/DEAL/KEYS" name="add-KEYS">
                <xsl:param name="KeyIdentifierTypeB">654321|1</xsl:param>
                <xsl:copy>
                    <xsl:copy-of select="*"/>
                    <KEY>
                        <KeyIdentifierType>KeyIdentifierTypeB</KeyIdentifierType>
                        <KeyValue>
                            <xsl:value-of select="$KeyIdentifierTypeB"/>
                        </KeyValue>
                    </KEY>
                </xsl:copy>
            </xsl:template>
        </xsl:stylesheet>
    

    become:

            <?xml version="1.0" encoding="UTF-8"?>
            <ROOT>
                <A>some A text</A>
                <B>some B text</B>
                <B1 xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">some B1 text</B1>
                <C>some C text</C>
                <D>some D text</D>
                <E>
                    <OTHER>
                        <DEAL>
                            <KEYS>
                                <KEY>
                                    <KeyIdentifierType>KeyIdentifierTypeA</KeyIdentifierType>
                                    <KeyValue>123456|1</KeyValue>
                                </KEY>
                            </KEYS>
                        </DEAL>
                    </OTHER>
                </E>
            </ROOT>
    

    why the second template definition has been completely ignored?

    The first code college has been resolved B1 node is set after B node and before node C, D and E or in the other words B1 node is set and nodes that must be places after it are: C, D and E.
    The second template match="ROOT/E/OTHER/DEAL/KEYS" that should satisfy the second challenge part: to Add the second KEY node to the /ROOT/E/OTHER/DEAL/KEYS structure, has been ignored completely. In addition to this fact, if you comment the first template match over the ROOT node, the second template match="ROOT/E/OTHER/DEAL/KEYS" will work correctly, it will actually add the additional key, but I don't know why the first template match always overrides the second one. I try the xsl:template match="ROOT/E/OTHER/DEAL/KEYS... and the xsl:for-each select=... and xsl:call-template name="add-KEYS" but nothing helped me out.

    I actually understand, that apply-templates, match nodes templates with higher structure with highest priority. Changing template place in the XSLT file will not have any impact, it will not read it in the exact line sequence, instead it will process it by matching priority. “apply-templates” for each matching template will made a change on the XML structure, implicitly it will made the "for-each" for us, but I don't how to "advice" the second template that the structure has been changed, and why I should do it, because the second template match is looking for a different XPath location, that btw has never been touched from the first template. Should I apply the template sequencing in my case?... and what is the best practice how to do it?

    Expected result:

            <?xml version="1.0" encoding="UTF-8"?>
            <ROOT>
                <A>some A text</A>
                <B>some B text</B>
                <B1>some B1 text</B1>
                <C>some C text</C>
                <D>some D text</D>
                <E>
                    <OTHER>
                        <DEAL>
                            <KEYS>
                                <KEY>
                                    <KeyIdentifierType>KeyIdentifierTypeA</KeyIdentifierType>
                                    <KeyValue>123456|1</KeyValue>
                                </KEY>
                                <KEY>
                                    <KeyIdentifierType>KeyIdentifierTypeB</KeyIdentifierType>
                                    <KeyValue>654321|1</KeyValue>
                                </KEY>
                            </KEYS>
                        </DEAL>
                    </OTHER>
                </E>
            </ROOT>