Reputation: 3887
I need to traverse an XML tree and ensure that some elements have an attribute attached to them, adding it if it's missing. The elements which need these attributes all have a common element name prefix and all elements with this prefix need the attribute.
Here's a contrived example, as I'm not able to share the actual XML:
<people>
<person>
<PRJ_NAME>Bob</PRJ_NAME>
<PRJ_AGE>22</PRJ_AGE>
<address>
<PRJ_FIRST_LINE>1 Test Street</PRJ_FIRST_LINE>
<PRJ_SECOND_LINE>London</PRJ_SECOND_LINE>
</address>
</person>
<person>
<PRJ_NAME>Tony</PRJ_NAME>
<PRJ_AGE>43</PRJ_AGE>
<address>
<PRJ_FIRST_LINE>23 Test Lane</PRJ_FIRST_LINE>
<PRJ_SECOND_LINE>Edinburgh</PRJ_SECOND_LINE>
</address>
</person>
</people>
The desired output is to add an m
(modified) attribute to all elements prefixed with PRJ_
. These elements are nested to different levels, and the tree is non-trivial, so I can't just hard-code the paths:
<people>
<person>
<PRJ_NAME m="2015-11-09">Bob</PRJ_NAME>
<PRJ_AGE m="2015-11-09">22</PRJ_AGE>
<address>
<PRJ_FIRST_LINE m="2015-11-09">1 Test Street</PRJ_FIRST_LINE>
<PRJ_SECOND_LINE m="2015-11-09">London</PRJ_SECOND_LINE>
</address>
</person>
<person>
<PRJ_NAME m="2015-11-09">Tony</PRJ_NAME>
<PRJ_AGE m="2015-11-09">43</PRJ_AGE>
<address>
<PRJ_FIRST_LINE m="2015-11-09">23 Test Lane</PRJ_FIRST_LINE>
<PRJ_SECOND_LINE m="2015-11-09">Edinburgh</PRJ_SECOND_LINE>
</address>
</person>
</people>
So far I've been working along the following lines based on other SO answers such as adding attribute to the node which works fine for a single element name, but I'm struggling to make it apply to patterns of element names:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:variable name="dateNow" select="format-date(current-date(), '[Y0001]-[M01]-[D01]')"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PRJ_FIRST_NAME">
<PRJ_FIRST_NAME m="{$dateNow}">
<xsl:apply-templates select="@*|node()"/>
</PRJ_FIRST_NAME>
</xsl:template>
</xsl:stylesheet>
Upvotes: 2
Views: 3811
Reputation: 167716
Change that template to
<xsl:template match="*[starts-with(local-name(), 'PRJ_') and not(@m)]">
<xsl:element name="{name()}">
<xsl:attribute name="m" select="$dateNow"/>
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
and your approach is fine, although I think to get the format you shown in the result sample you need to use <xsl:variable name="dateNow" select="format-date(current-date(), '[Y0001]-[M01]-[D01]')"/>
.
Online at http://xsltransform.net/94rmq63.
Upvotes: 3