Reputation: 124696
This is similar to this question: XSLT-1.0: How to insert an element at a specific position with respect to the parent element, but the answer there doesn't cover the case where tag3
is missing in the input XML
I want to insert or update a tag (say tag4) in the an XML document that looks like the following, except that tag1, tag2, tag3, tag4 and tag5 are all optional.
<Begin>
<tag1 value="a" />
<tag2 value="b" />
<tag3 value="c" />
<tag4 value="d" />
<tag5 value="e" />
</Begin>
I.e. the following are sample inputs and outputs
Input:
<Begin>
</Begin>
Output:
<Begin>
<tag4 value="updated" />
</Begin>
Input:
<Begin>
<tag4 value="d" />
</Begin>
Output:
<Begin>
<tag4 value="updated" />
</Begin>
Input:
<Begin>
<tag1 value="a" />
<tag5 value="e" />
</Begin>
Output:
<Begin>
<tag1 value="a" />
<tag4 value="updated" />
<tag5 value="e" />
</Begin>
Input:
<Begin>
<tag1 value="a" />
<tag2 value="b" />
<tag5 value="e" />
</Begin>
Output:
<Begin>
<tag1 value="a" />
<tag2 value="b" />
<tag4 value="updated" />
<tag5 value="e" />
</Begin>
Input:
<Begin>
<tag1 value="a" />
<tag2 value="b" />
<tag3 value="c" />
<tag5 value="e" />
</Begin>
Output:
<Begin>
<tag1 value="a" />
<tag2 value="b" />
<tag3 value="c" />
<tag4 value="updated" />
<tag5 value="e" />
</Begin>
UPDATE
I also want to be able to preserve any attributes that may already be present on the Begin or tag4 element, e.g.:
Input:
<Begin someAttribute="someValue">
<tag1 value="a" />
<tag2 value="b" />
<tag3 value="c" />
<tag4 value="d" someOtherAttribute="someOtherValue" />
<tag5 value="e" />
</Begin>
Output:
<Begin someAttribute="someValue">
<tag1 value="a" />
<tag2 value="b" />
<tag3 value="c" />
<tag4 value="updated" someOtherAttribute="someOtherValue" />
<tag5 value="e" />
</Begin>
Upvotes: 1
Views: 219
Reputation: 1235
Give this a try... Trace it through in a debugger to help you figure it out, if needed.
<xsl:template match="Begin">
<xsl:copy>
<!-- Apply any attributes for Begin. -->
<xsl:apply-templates select="@*"/>
<!-- Output the tag* nodes before tag4. -->
<xsl:apply-templates select="tag1|tag2|tag3"/>
<xsl:choose>
<!-- Is there already a tag4? -->
<xsl:when test="tag4">
<xsl:apply-templates select="tag4"/>
</xsl:when>
<xsl:otherwise>
<!-- Create tag4 -->
<tag4 value="updated"/>
</xsl:otherwise>
</xsl:choose>
<!-- Output the tag* nodes after tag4. -->
<xsl:apply-templates select="tag5"/>
</xsl:copy>
</xsl:template>
<xsl:template match="tag4">
<xsl:copy>
<xsl:attribute name="value">d</xsl:attribute>
<xsl:attribute name="someOtherAttribute">someOtherValue</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
Upvotes: 0
Reputation: 163272
Try:
<xsl:template match="Begin">
<Begin>
<xsl:copy-of select="tag1 | tag2 | tag3"/>
<tag4 value="updated"/>
<xsl:copy-of select="tag5"/>
</Begin>
</xsl:template>
Upvotes: 1