user3735276
user3735276

Reputation: 27

XSLT1.0 Add child element only once if does not exist

I am trying to add an element called Stamping one time and only if it doesn't exist in the message.

<!--  Copy all other elements  -->
<xsl:template match="@*|node()">
<xsl:copy>
    <xsl:apply-templates select="@*|node()" />
 </xsl:copy>

<xsl:template match="/*[local-name()='Envelope']/*[local-name()='Body']/*/*[not(*[local-name()='Stamping'])]">     
 <xsl:copy>
    <xsl:apply-templates select="@*|node()" />
 </xsl:copy>
  <v1:Stamping>
     <v2:UserData CityCode="NO Stamping" Role="User" SecurityId="55"/>
  </v1:Stamping>
</xsl:template>

My problem occurs because the new v1:Stamping value is repeated for every child like this based on my existing match:

<envelope>
<body>
<operationName>
 <child1 Catalog="1" />
 <v1:Stamping>
 <v2:UserData CityCode="NO Stamping" Role="User" SecurityId="55"/>
 <v1:Stamping>
 <child2 Catalog="2" />
 <v1:Stamping>
     <v2:UserData CityCode="NO Stamping" Role="User" SecurityId="55"/>
 </v1:Stamping>
</operationName>
</body>
</envelope>

I need to see the result where stamping is only added one time as a child element of operationName. operationName is an element that will exist but this XSLT will be applied across many services and the value in operationName will be different depending on the service where this XSLT will be applied. This example below would be the needed output.

<envelope>
<body>
<operationName>
 <child1 Catalog="1" />
 <child2 Catalog="2" />
 <v2:UserData CityCode="NO Stamping" Role="User" SecurityId="55"/>
 </operationName>
</body>
</envelope>

Upvotes: 0

Views: 1132

Answers (1)

Mathias M&#252;ller
Mathias M&#252;ller

Reputation: 22617

I need to see the result where stamping is only added one time as a child element of operationName.

Then write a template especially for operationName elements:

<xsl:template match="operationName">

and check whether v1:Stamping already exists as one of its children:

<xsl:if test="not(v1:Stamping)">

If it does not exist already, it is added to the output tree.

Assuming the following XML, where there is no v1:Stamping element, as input:

XML Input

<?xml version="1.0" encoding="UTF-8"?>
<envelope>
   <body>
      <operationName>
         <child1 Catalog="1"/>
         <child2 Catalog="2"/>
      </operationName>
   </body>
</envelope>

Stylesheet

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

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

    <xsl:template match="operationName">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
            <xsl:if test="not(v1:Stamping)">
                <v1:Stamping>
                    <v2:UserData CityCode="NO Stamping" Role="User" SecurityId="55"/>
                </v1:Stamping>
            </xsl:if>
        </xsl:copy>
    </xsl:template>

</xsl:transform>

XML Output

<?xml version="1.0" encoding="UTF-8"?>
<envelope>
   <body>
      <operationName>
         <child1 Catalog="1"/>
         <child2 Catalog="2"/>
         <v1:Stamping xmlns:v1="http://www.v1.com" xmlns:v2="http://www.v2.com">
            <v2:UserData CityCode="NO Stamping" Role="User" SecurityId="55"/>
         </v1:Stamping>
      </operationName>
   </body>
</envelope>

Upvotes: 1

Related Questions