属性值的 xpath 部分?

xpath part of attribute value?

输入xml文件:

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

大括号指示属性的 XPath,而不是您必须沿此 XPath 替换属性的值,例如, 代替 {@key} 和 {../@key},替换当前节点的 key 属性的值。 XSLT1.0 转换后的文件应该是这样的:

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

现在这是 XSLT1.0 转换文件:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common" version="1.0">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="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, '{') ">
            <xsl:value-of select="substring-before($text, '{')"/>
            <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>            
            <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>

Xml 转换后的文件:

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

如何正确生成属性值的 xpath 部分?

如前所述,您需要 XSLT 3 中的 xsl:evaluate 或 XSLT 2 或 1 的类似扩展;此外,您需要找到要评估的 {...} 个“模板”,并且仅当引用的属性值已被计算时,您才需要实施一个评估“模板”的策略。

使用 XSLT 3 我达到了:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="#all"
    xmlns:mf="http://example.com/mf"
    version="3.0">
  
  <xsl:param name="avt-pattern" as="xs:string" expand-text="no">\{(.*?)\}</xsl:param>

  <xsl:mode name="convert" on-no-match="shallow-copy"/>

  <xsl:template mode="convert" match="@*[matches(., $avt-pattern)]">
    <xsl:variable name="context" select=".."/>
    <xsl:attribute name="{name()}">
      <xsl:analyze-string select="." regex="{$avt-pattern}">
        <xsl:matching-substring>
          <xsl:variable name="evaluated-result" as="xs:string">
            <xsl:evaluate xpath="regex-group(1)" context-item="$context"/>
          </xsl:variable>
          <xsl:value-of select="if (matches($evaluated-result, $avt-pattern)) then . else $evaluated-result"/>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
          <xsl:value-of select="."/>
        </xsl:non-matching-substring>
      </xsl:analyze-string>
    </xsl:attribute>
  </xsl:template>
  
  <xsl:function name="mf:evaluate-attributes" as="document-node()">
    <xsl:param name="input" as="node()"/>
    <xsl:variable name="result" as="node()">
      <xsl:apply-templates select="$input" mode="convert"/>
    </xsl:variable>
    <xsl:sequence
      select="if ($result//@*[matches(., $avt-pattern)])
              then mf:evaluate-attributes($result)
              else $result"/>
  </xsl:function>
  
  <xsl:template match="/">
    <xsl:sequence select="mf:evaluate-attributes(.)"/>
  </xsl:template>
  
</xsl:stylesheet>

在线示例 https://xsltfiddle.liberty-development.net/jy6KM8D

使用 XSLT 1.0 和 Microsoft 可以在项目 https://www.nuget.org/packages/Mvp.Xml.NetStandard 的帮助下完成此操作,然后

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exslt="http://exslt.org/common"
    exclude-result-prefixes="exslt regexp dyn2 mf"
    xmlns:mf="http://example.com/mf"
    xmlns:regexp="http://exslt.org/regular-expressions"
    xmlns:dyn2="http://gotdotnet.com/exslt/dynamic"
    version="1.0">
  
  <xsl:param name="avt-pattern">\{(.*?)\}</xsl:param>
  
  <xsl:template mode="convert" match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()" mode="convert"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="evaluate-avts">
    <xsl:param name="attribute-value"/>
    <xsl:param name="context"/>
    <xsl:choose>
      <xsl:when test="contains($attribute-value, '{')">
        <xsl:value-of select="substring-before($attribute-value, '{')"/>
        <xsl:variable name="remainder" select="substring-after($attribute-value, '{')"/>
        <xsl:variable name="avt" select="substring-before($remainder, '}')"/>
        <xsl:call-template name="evaluate-avt">
          <xsl:with-param name="expression" select="$avt"/>
          <xsl:with-param name="context" select="$context"/>
        </xsl:call-template>
        <xsl:call-template name="evaluate-avts">
          <xsl:with-param name="attribute-value" select="substring-after($remainder, '}')"/>
          <xsl:with-param name="context" select="$context"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$attribute-value"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template name="evaluate-avt">
    <xsl:param name="expression"/>
    <xsl:param name="context"/>
    <xsl:variable name="evaluated-result" select="dyn2:evaluate($context, $expression)"/>
    <xsl:choose>
      <xsl:when test="regexp:test($evaluated-result, $avt-pattern)">
        <xsl:value-of select="concat('{', $expression, '}')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$evaluated-result"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template mode="convert" match="@*">
    <xsl:choose>
      <xsl:when test="regexp:test(., $avt-pattern)">
        <xsl:variable name="context" select=".."/>
        <xsl:attribute name="{name()}">
          <xsl:call-template name="evaluate-avts">
            <xsl:with-param name="attribute-value" select="."/>
            <xsl:with-param name="context" select="$context"/>
          </xsl:call-template>
        </xsl:attribute>        
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template mode="evaluate-attributes" match="/">
    <xsl:variable name="result-rtf">
      <xsl:apply-templates select="." mode="convert"/>
    </xsl:variable>
    <xsl:variable name="result" select="exslt:node-set($result-rtf)"/>
    <xsl:choose>
      <xsl:when test="$result//@*[regexp:test(., $avt-pattern)]">
        <xsl:apply-templates select="$result" mode="evaluate-attributes"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="$result"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  
  <xsl:template match="/">
    <xsl:apply-templates select="." mode="evaluate-attributes"/>
  </xsl:template>
  
</xsl:stylesheet>