I am using xmlstarlet which supports only xsl 1.0 to transform one xml to another.
I am trying to find the value of attribute 'atr1' and insert another attribute 'atr2' in the same node.
When the below sample.xml is used
<a>
<b></b>
<c>
<d atr1="#{not MGR_1}" atr2="#{condition1}"></d>
<d atr1="#{ MGR_2}" ></d>
<d atr1="2"></d>
</c>
</a>
With the following transform.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="d[contains(@atr1, 'MGR_')]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
<xsl:attribute name="atr2">
<xsl:choose>
<xsl:when test="@atr2">
<xsl:value-of select="concat('#{', substring(@atr2,3,string-length(@atr2)-3), ' and not ', substring(@atr1,3))" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('#{not ', substring(@atr1,3))" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I get the expected o/p as below.
<a>
<b/>
<c>
<d atr1="#{not MGR_1}" atr2="#{condition1 and not not MGR_1}"/>
<d atr1="#{ MGR_2}" atr2="#{not MGR_2}"/>
<d atr1="2"/>
</c>
</a>
But when I try to use the below xml. Which has an other inner element x, then the problem arises.
<a>
<b></b>
<c>
<d atr1="#{not MGR_1}" atr2="#{condition1}"></d>
<d atr1="#{ MGR_2}" ></d>
<d atr1="2"></d>
<d atr1="#{ MGR_3}" >
<x>blah blah blah</x>
</d>
</c>
</a>
On trying to transform the above XML. I get a error message as
$ xml tr transform.xsl sample.xml
runtime error: file transform.xsl line 14 element attribute
xsl:attribute: Cannot add attributes to an element if children have been already added to the element.
Where am I getting it wrong. What could be the correct XSL to get the desired o/p on the modified XML.
The error message you get actually speaks for itself. You cannot add attributes if child nodes are already assigned to a node.
Simply separate applying a template to all attributes @*
from applying a template to all potential child nodes node()
. Note that this captures not only child elements.
By the way, I could reproduce your error message only with Saxon 9.5.1 but not with Saxon 6.5.5 and Xalan 2.7.1.
Stylesheet
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="d[contains(@atr1, 'MGR_')]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="atr2">
<xsl:choose>
<xsl:when test="@atr2">
<xsl:value-of select="concat('#{', substring(@atr2,3,string-length(@atr2)-3), ' and not ', substring(@atr1,3))" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('#{not ', substring(@atr1,3))" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>