开发者

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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜