XSLT Transformation to add multiple not existing child elements

xml, xslt

Solution

I would do it like this:

<xsl:template match="p"> 
  <xsl:copy> 
    <xsl:apply-templates select="@*|node()"/> 
    <xsl:if test="not(c1)">
      <c1 /> 
    </xsl:if>
    <xsl:if test="not(c2)">
      <c2 /> 
    </xsl:if>
  </xsl:copy> 
</xsl:template> 

If you have a longer list of possible child nodes you can put them in a variable and use a `for-each` instead than individual `if`:

<xsl:stylesheet 
  version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
  exclude-result-prefixes="msxsl">

  <xsl:output method="xml" indent="yes"/>

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

  <xsl:variable name="childrenFragment">
    <c1/>
    <c2/>
  </xsl:variable>

  <xsl:template match="p">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:variable name="this" select="."/>
      <xsl:for-each select="msxsl:node-set($childrenFragment)/*">
        <xsl:variable name="localName" select="local-name()"/>
        <xsl:if test="not($this/*[local-name()=$localName])">
          <xsl:element name="{$localName}"/>
        </xsl:if>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

just add all the elements you need in the `childrenFragment` variable.

(the `msxsl:node-set` stuff is Microsoft-specific, if you are using another XSLT processor you'll need something slightly different)

Problem

I've got a xml document, looking like this: ``` <p> <c1 /> <c2 /> </p> ``` The child elements c1 and c2 are optional, but for a processing step I need them to be existent. So I am trying to create a xslt stylesheet to add them as empty elements (the order of the children does not matter). Here is my stylesheet: ``` <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="p[not(c1)]"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> <c1 /> </xsl:copy> </xsl:template> <xsl:template match="p[not(c2)]"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> <c2 /> </xsl:copy> </xsl:template> ``` This works fine, as long as only one of the child elements is missing. But if both are missing, only c1 is created. How do I prevent that and force the creation of both c1 and c2 (and in reality, of about 10 children)? Thanks. Jost

Original source