Reputation: 6831
I found a similar question here:
How to read attribute of a parent node from a child node in XSLT
But not exactly what I need here. Suppose using the same example here:
<A>
<b attr1="xx">
<c>
Turn this into an Attribute
</c>
</b>
</A>
and I want the resulting xml after xslt looks like:
<A>
<b attr1="xx" cAttr="Turn this into an Attribute">
</b>
</A>
Using my current knowledge, I could only manage to get rid of the node or change its name to the desired name "cAttr", but I really have no idea of how to turn the whole node into an attribute of the parent node, by only knowing how to refer to attribute field of the parent node won't help me a lot here.
My current code just looks like this:
<xsl:template match="c">
<xsl:element name="cAttr">
<xsl:apply-templates select="@*|node()"/>
</xsl:element>
</xsl:template>
Thanks in advance.
Upvotes: 0
Views: 421
Reputation:
Besides @Flack correct push style, and just for fun, two pull style approaches:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b/*[1][self::c]" name="attribute" priority="1">
<xsl:attribute name="cAttr">
<xsl:value-of select="../c"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="b[c]/*[1]">
<xsl:call-template name="attribute"/>
<xsl:call-template name="identity"/>
</xsl:template>
<xsl:template match="b/c"/>
</xsl:stylesheet>
Note: Only rules with pattern matching overwriting the identity rule.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b[c]/*[1]" priority="1">
<xsl:attribute name="cAttr">
<xsl:value-of select="../c"/>
</xsl:attribute>
<xsl:if test="not(self::c)">
<xsl:call-template name="identity"/>
</xsl:if>
</xsl:template>
<xsl:template match="b/c"/>
</xsl:stylesheet>
Note: One rule but with some xsl:if
.
Both output:
<A>
<b attr1="xx" cAttr="Turn this into an Attribute"></b>
</A>
Edit: Oops! I've missed the stripping rule.
Upvotes: 1
Reputation: 5892
I prefer a more concise (though less generic may be) design:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="b">
<xsl:copy>
<xsl:copy-of select="@*"/>
<xsl:attribute name="cAttr">
<xsl:value-of select="normalize-space(c)"/>
</xsl:attribute>
<xsl:apply-templates select="*[not(self::c)]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to XML like this:
<A>
<b attr1="xx">
<d>SomeNode</d>
<c>
Turn this into an Attribute
</c>
<f>SomeMoreNode</f>
</b>
</A>
Result will be:
<A>
<b attr1="xx" cAttr="Turn this into an Attribute">
<d>SomeNode</d>
<f>SomeMoreNode</f>
</b>
</A>
Upvotes: 0
Reputation: 607
<!-- match b node -->
<xsl:template match="b">
<!-- apply templates on all attributes and nodes. see two templates below -->
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
<!-- copy all existing attrs -->
<xsl:template match="b/@*">
<xsl:copy-of select="."/>
</xsl:template>
<!-- populate attributes from nodes -->
<xsl:template match="b/node()">
<xsl:attribute name="{name()}Attr"> <!-- attribute name -->
<xsl:value-of select="text()"/> <!-- attribute value -->
</xsl:attribute>
<!-- match all attributes on child node -->
<xsl:apply-templates select="@*">
<xsl:with-param name="prefix" select="name()"/> <!-- pass node name to template -->
</xsl:apply-templates>
</xsl:template>
<xsl:template match="b/node()/@*">
<xsl:param name="prefix"/>
<!-- creates attribute prefixed with child node name -->
<xsl:attribute name="{$prefix}-{name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
Upvotes: 2
Reputation: 11561
Not as nice as Gleb's version, but why not post it if I've already wasted my time= ;)
<!-- Identity template -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<!-- assemble b element -->
<xsl:template match="b">
<b>
<xsl:attribute name="attr1">
<xsl:value-of select="@attr1" />
</xsl:attribute>
<xsl:attribute name="cAttr">
<xsl:value-of select="c" />
</xsl:attribute>
</b>
</xsl:template>
<!-- ignore c element -->
<xsl:template match="c" />
Upvotes: 0