Reputation: 3442
I am writing a transformation where I would like to add a xmlns attribute (xmlns="myNS"
) to the root node.
When applying the transformation the xmlns
attribute is included in some of the child elements.
I can't work out how to change my transformation to only apply it to the root element.
XML
<db:result xmlns:db="http://www.sonicsw.com/esb/service/dbservice">
<db:resultSet version="1.1">
<db:row>
<id>a</id>
<value1>b</value1>
<value2>c</value2>
</db:row>
<db:row>
<id>a</id>
<value1>d</value1>
<value2>e</value2>
</db:row>
<db:row>
<id>a</id>
<value1>f</value1>
<value2>g</value2>
</db:row>
<db:row>
<id>a</id>
<value1>h</value1>
<value2>i</value2>
</db:row>
</db:resultSet>
</db:result>
XSLT
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:db="http://www.sonicsw.com/esb/service/dbservice"
exclude-result-prefixes="db">
<xsl:template match="/">
<xsl:for-each-group select="//db:row" group-by="id">
<xsl:sort select="id"/>
<xsl:apply-templates select="." mode="document"/>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="db:row" mode="document">
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="myNS">
<id><xsl:value-of select="id"/></id>
<lines>
<xsl:apply-templates select="//db:row[id=current()/id]" mode="line"/>
</lines>
</root>
</xsl:template>
<xsl:template match="db:row" mode="line">
<line>
<valueof1><xsl:value-of select="value1"/></valueof1>
<valueof2><xsl:value-of select="value2"/></valueof2>
</line>
</xsl:template>
</xsl:stylesheet>
XML Output
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="myNS">
<id>a</id>
<lines>
<line xmlns="">
<valueof1>b</valueof1>
<valueof2>c</valueof2>
</line>
<line xmlns="">
<valueof1>d</valueof1>
<valueof2>e</valueof2>
</line>
<line xmlns="">
<valueof1>f</valueof1>
<valueof2>g</valueof2>
</line>
<line xmlns="">
<valueof1>h</valueof1>
<valueof2>i</valueof2>
</line>
</lines>
</root>
XML Expected Output
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="myNS">
<id>a</id>
<lines>
<line>
<valueof1>b</valueof1>
<valueof2>c</valueof2>
</line>
<line>
<valueof1>d</valueof1>
<valueof2>e</valueof2>
</line>
<line>
<valueof1>f</valueof1>
<valueof2>g</valueof2>
</line>
<line>
<valueof1>h</valueof1>
<valueof2>i</valueof2>
</line>
</lines>
</root>
Note: I found this existing post but can't workout how to apply the suggested solution:
Upvotes: 1
Views: 466
Reputation: 122394
Literal result elements in a stylesheet take their namespace from the xmlns
declarations in scope at that point in the stylesheet, i.e. within
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="myNS">
<id><xsl:value-of select="id"/></id>
<lines>
<xsl:apply-templates select="//db:row[id=current()/id]" mode="line"/>
</lines>
</root>
the root
element and all its unprefixed children are in the myNS
namespace. However in
<xsl:template match="db:row" mode="line">
<line>
<valueof1><xsl:value-of select="value1"/></valueof1>
<valueof2><xsl:value-of select="value2"/></valueof2>
</line>
</xsl:template>
the line
and valueofN
elements are in no namespace, since there's no default xmlns
in scope at this point in the stylesheet.
So the simple answer is to move the xmlns="myNS"
from the root
element in the db:row
template up into the top-level xsl:stylesheet
instead:
<xsl:stylesheet version="2.0"
xmlns="myNS"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:db="http://www.sonicsw.com/esb/service/dbservice"
exclude-result-prefixes="db">
<xsl:template match="/">
<xsl:for-each-group select="//db:row" group-by="id">
<xsl:sort select="id"/>
<xsl:apply-templates select="." mode="document"/>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="db:row" mode="document">
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<id><xsl:value-of select="id"/></id>
<lines>
<xsl:apply-templates select="//db:row[id=current()/id]" mode="line"/>
</lines>
</root>
</xsl:template>
<xsl:template match="db:row" mode="line">
<line>
<valueof1><xsl:value-of select="value1"/></valueof1>
<valueof2><xsl:value-of select="value2"/></valueof2>
</line>
</xsl:template>
</xsl:stylesheet>
Though note that this stylesheet will not produce well-formed output if the row
elements in the original XML don't all have the same id, as you'll get multiple root
elements with no single parent. You may wish to add a wrapping element in the match="/"
template to ensure the output is well formed.
P.S. since you're in a for-each-group
it may be more efficient to lose the mode="document"
template and move its content directly inside the f-e-g, then use current-group
instead of having to filter for [id=current()/id]
:
<xsl:template match="/">
<xsl:for-each-group select="//db:row" group-by="id">
<xsl:sort select="current-grouping-key()"/>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<id><xsl:value-of select="current-grouping-key()"/></id>
<lines>
<xsl:apply-templates select="current-group()" mode="line"/>
</lines>
</root>
</xsl:for-each-group>
</xsl:template>
Upvotes: 2