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