递归属性计算?

recursive attribute calculation?

XML 输入:

<a>
  <desc key="1" attr1="# {@key}" attr2="{@key} #"  attr3="" title="{@attr2}">
    <b>
      <slot key="2" attr4="{@key}" title="{@attr4}+{@attr4}" >
        <val key="3">
          <fix key="4" title11="{@key} {@key}" title="{@key}+{@title11}" >
            <c></c>
          </fix>
        </val>
        <numb key="5" title12="z{@key}z in {@key}" title="{@title12}+{@key}" titlew="{@title}+{@title12}">
        </numb>
      </slot>
    </b>
  </desc>
</a>

我有这个 XML 转换代码 XSLT1.0:

<?xml version="1.0" encoding="utf-8"?>
 
<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="*/@*">
    <xsl:attribute name="{name()}">
        <xsl:call-template name="replace">
          <xsl:with-param name="str" select="."/>
          <xsl:with-param name="find" select="'{@key}'"/>
          <xsl:with-param name="replace" select="../@key"/>
        </xsl:call-template>
    </xsl:attribute>
  </xsl:template>
  
    <!--Identity template-->
  <xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="replace">
    <xsl:param name="str"/>
    <xsl:param name="find"/>
    <xsl:param name="replace"/>
    <xsl:choose>
      <xsl:when test="contains($str, $find)">
        <xsl:variable name="prefix" select="substring-before($str, $find)"/>
        <xsl:variable name="suffix">
          <xsl:call-template name="replace">
            <xsl:with-param name="str" select="substring-after($str, $find)"/>
            <xsl:with-param name="find" select="$find"/>
            <xsl:with-param name="replace" select="$replace"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="concat($prefix, $replace, $suffix)"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$str"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

此代码查找标签属性值中出现的任何“{@key}”。如果标记在“@”字符后指定了属性,即在本例中为 'key',则字符串“{@key}”将替换为 'key' 属性的值。那么转换后的XML文件会是这样的:

<a>
  <desc key="1" attr1="# 1" attr2="1 #" attr3="" title="{@attr2}">
    <b>
      <slot key="2" attr4="2" title="{@attr4}+{@attr4}">
        <val key="3">
          <fix key="4" title11="4 4" title="4+{@title11}">
            <c />
          </fix>
        </val>
        <numb key="5" title12="z5z in 5" title="{@title12}+5" titlew="{@title}+{@title12}">
        </numb>
      </slot>
    </b>
  </desc>
</a>

但我不仅需要用 'key' 属性的值替换字符串 '{@key}',而且还需要一个更通用的规则:如果标签具有属性 'attr',然后值标记属性中出现的任何字符串“{@attr}”都被替换为 'attr' 属性的值。有没有办法使用 XSLT 1.0 执行此操作,以便在转换后 XML 文件如下所示:

<a>
  <desc key="1" attr1="# 1" attr2="1 #"  attr3="" title="1 #">
    <b>
      <slot key="2" attr4="2" title="2+2" >
        <val key="3">
          <fix key="4" title11="4 4" title="4+4 4" >
            <c></c>
          </fix>
        </val>
        <numb key="5" title12="z5z in 5" title="z5z in 5+5" titlew="z5z in 5+5+z5z in 5">
        </numb>
      </slot>
    </b>
  </desc>
</a>

如果可以假设一个属性永远不会包含 {} 个字符,而不是包含对同一父元素的另一个属性的引用,那么您可以这样做:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="@*">
    <xsl:attribute name="{name()}">
        <xsl:call-template name="expand">
            <xsl:with-param name="text" select="."/>
        </xsl:call-template>
    </xsl:attribute>
</xsl:template>

<xsl:template name="expand">
    <xsl:param name="text"/>
    <xsl:choose>
        <xsl:when test="contains($text, '{') ">
            <!-- text before reference -->
            <xsl:value-of select="substring-before($text, '{')"/>
            <!-- recursive call with the expanded reference-->
            <xsl:variable name="name" select="substring-before(substring-after($text, '{@'), '}')" />
            <xsl:call-template name="expand">
                <xsl:with-param name="text" select="../@*[name()=$name]"/>
            </xsl:call-template>            
            <!-- recursive call with rest of the text -->
            <xsl:call-template name="expand">
                <xsl:with-param name="text" select="substring-after($text, '}')"/>
            </xsl:call-template>            
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$text"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

注意:没有经过非常彻底的测试。