Reputation: 1054
I have an XML document that I want to generate unique IDs for. Some nodes may already have the attribute in which case this is to be replaced. I want all nodes in the document to have the attribute.
An example document would be
<root>
<anode uid='123'/>
<anode/>
</root>
I am using the following stylesheet
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:attribute name="uid">
<xsl:value-of select="generate-id(.)"/>
</xsl:attribute>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
And am getting the following output. This is close to what I want, but how do I prevent the existing ID from being created as a text node?
<root uid="id515559">
<anode uid="id515560">123</anode>
<anode uid="id515562"/>
</root>
I have looked at XSLT: How to change an attribute value during <xsl:copy>? but I couldn't get this to create new attributes.
If it makes a difference I'm using lxml to process the stylesheet.
Upvotes: 2
Views: 395
Reputation: 52888
The answer is not really as extreme as adding a new match to override the @uid
. You just need to remove the select
from your xsl:apply-templates
.
Your stylesheet with the select
removed:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:attribute name="uid">
<xsl:value-of select="generate-id(.)"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
produces the following output (@uid
will vary):
<root uid="d0e1">
<anode uid="d0e3"/>
<anode uid="d0e5"/>
</root>
Upvotes: 0
Reputation: 6956
The Built-in template rule is being applied to the input @uid
by the line:
<xsl:apply-templates select="@*|node()"/>
The Default template for an attribute copies the text, behaving as though you had defined a template thus:
<xsl:template match="text()|@*">
<xsl:value-of select="."/>
</xsl:template>
In order to prevent this being applied, either change your apply-templates
select attribute so that it does not apply to attributes, or define a new blank template for any attribute, thus:
<xsl:template match="@*" />
You could even be more specific, using a similar template to ignore uid
attributes only, thus:
<xsl:template match="@uid" />
Upvotes: 0
Reputation: 70648
All you need to do is add a template to match the existing uid attribute, and ignore it...
<xsl:template match="@uid" />
So, with this stylesheet...
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes"/>
<xsl:template match="*">
<xsl:copy>
<xsl:attribute name="uid">
<xsl:value-of select="generate-id(.)"/>
</xsl:attribute>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@uid" />
</xsl:stylesheet>
When applied to your sample XML, the output is as follows:
<root uid="IDAEQLT">
<anode uid="IDA3XLT"></anode>
<anode uid="IDA0XLT"></anode>
</root>
Upvotes: 1