XSLT: recursively concatenate parent node attribut

2019-09-02 15:49发布

问题:

I'd like to concatenate recursively the parameter value of a node with the value of the same parameter of its parent node.

Example, the following:

<node name="root">
  <node name="A">
    <node name="B">
      <node name="C">
        <value1>12</value1>
        <value2>36</value1>
      </node>
    </node>
  </node>
</node>

Should become

<node name="root">
  <node name="root.A">
    <node name="root.A.B">
      <node name="root.A.B.C">
        <value1>12</value1>
        <value2>36</value1>
      </node>
    </node>
  </node>
</node>

I tried with

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>
<xsl:template match="node/@name">
  <xsl:choose>
    <xsl:when test=". = 'root'">
      <xsl:attribute name="name"><xsl:text>webshop</xsl:text></xsl:attribute>
    </xsl:when>
    <xsl:when test="not(. = 'root')">
      <xsl:attribute name="name"><xsl:value-of select="../../@name"/>.<xsl:value-of select="../@name"/></xsl:attribute>
    </xsl:when>
  </xsl:choose>
</xsl:template>

But the result is not the one expected. What I get is

<node name="root">
  <node name="root.A">
    <node name="A.B">
      <node name="B.C">
        <value1>12</value1>
        <value2>36</value1>
      </node>
    </node>
  </node>
</node>

What's the problem?

回答1:

XSLT operates on an input document to produce a new output document. The input document itself will remain unchanged. When you do xsl:value-of (or any other XSLT operation), you will be selecting from the input document. So, the fact you are adding values to the name attribute won't affect your xsl:value-of statement at all.

What you can do is use a simple xsl:for-each to get all the ancestor nodes and build the name attribute from that:

<xsl:for-each select="ancestor::node">
  <xsl:if test="position() > 1">.</xsl:if>
  <xsl:value-of select="@name" />
</xsl:for-each>

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node/@name">
    <xsl:attribute name="name">
      <xsl:choose>
        <xsl:when test=". = 'root'">webshop</xsl:when>
        <xsl:otherwise>
          <xsl:for-each select="ancestor::node">
            <xsl:if test="position() > 1">.</xsl:if>
            <xsl:value-of select="@name" />
          </xsl:for-each>
       </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
  </xsl:template>
</xsl:stylesheet>

If you could use XSLT 2.0, you could replace the xsl:for-each with a single xsl:value-of

<xsl:value-of select="ancestor::node/@name" separator="." />

(In XSLT 1.0, xsl:value-of only outputs the first node in the set)



标签: xml xslt