clsturgeon
clsturgeon

Reputation: 302

What is needed in XSLT to add missing XML element?

I am trying to transform an XML file into an updated version.

I have the following Source XML

<?xml version="1.0" encoding="utf-8" ?>
<config>
  <section name="abc">
    <key name="key1">My Key One</key>
  </section>
  <section name="def">
    <key name="key2">My Key Two</key>
  </section>
</config>

The transformation is to look like this... however, the target actually may already look like this, so I do not want section xyz added if already exists:

<?xml version="1.0" encoding="utf-8" ?>
<config>
  <section name="abc">
    <key name="key1">My Key One</key>
  </section>
  <section name="def">
    <key name="key2">My Key Two</key>
  </section>
  <section name="xyz">
    <key name="key3">My Key Three</key>
  </section>
</config>

XSLT currently looks like this:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="config">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>

<xsl:template match="@*|text()|comment()|processing-instruction()">
<xsl:copy-of select="."/>
</xsl:template>

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

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

</xsl:stylesheet>

This appears to replicate what exists. How do I get it to add my missing element if it is in fact missing?

Upvotes: 4

Views: 5116

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243469

Shorter and simpler (no explicit conditional instructions):

<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="config[not(section[@name = 'xyz'])]">
  <config>
   <xsl:apply-templates select="node()|@*"/>
   <section name="xyz">
     <key name="key3">My Key Three</key>
   </section>
  </config>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided source XML document:

<config>
    <section name="abc">
        <key name="key1">My Key One</key>
    </section>
    <section name="def">
        <key name="key2">My Key Two</key>
    </section>
</config>

the wanted, correct result is produced (a new section is added):

<config>
   <section name="abc">
      <key name="key1">My Key One</key>
   </section>
   <section name="def">
      <key name="key2">My Key Two</key>
   </section>
   <section name="xyz">
      <key name="key3">My Key Three</key>
   </section>
</config>

when applied on this XML document:

<config>
   <section name="abc">
      <key name="key1">My Key One</key>
   </section>
   <section name="def">
      <key name="key2">My Key Two</key>
   </section>
   <section name="xyz">
      <key name="key3">My Key Three</key>
   </section>
</config>

again the wanted result is produced (a new section isn't added, because it already exists):

<config>
   <section name="abc">
      <key name="key1">My Key One</key>
   </section>
   <section name="def">
      <key name="key2">My Key Two</key>
   </section>
   <section name="xyz">
      <key name="key3">My Key Three</key>
   </section>
</config>

Upvotes: 4

Daniel Haley
Daniel Haley

Reputation: 52858

Try something like this:

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

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

  <xsl:template match="config">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
      <xsl:if test="not(section/@name='xyz')">
        <section name="xyz">
          <key name="key3">My Key Three</key>
        </section>
      </xsl:if>
    </xsl:copy>    
  </xsl:template>

</xsl:stylesheet>

Upvotes: 4

Related Questions