How to Turn a Child Node Into an Attribute of the Current Node
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 ho开发者_运维问答w 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.
<!-- 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>
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.
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" />
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>
精彩评论