XSLT 中的表达式 $data[not(key('myKey',@myRef))] 是什么意思?

What does the expression $data[not(key('myKey',@myRef))] in XSLT mean?

我读过这样的表达方式

<xsl:variable name="myVar" select="$data[not(key('myKey',@myRef))]"/>

在遗留代码中。最有可能是来自专家的代码 ;-)。我想知道它是做什么的,它是如何工作的,以及我如何重新设计它以使其更具可读性。谢谢。

键是 XSLT 的一个重要方面。与其重新设计它们,不如学习这个概念。

Keys可以理解为tables,节点存储在特定的keys下。它们是这样定义的:

<xsl:key name="addressByStreet" match="address" use="street"/>

name 属性只是一个 QName(类似于变量名)。 match 属性包含一个 XPath 表达式,其工作方式类似于 <xsl:template>match 属性。当处理器找到与表达式匹配的节点时,它会在匹配元素的上下文中计算 use 属性的 XPath 表达式。如果此表达式 returns 值,它们将用于在 "key table" 中为匹配元素创建新条目。

为了说明这一点:上面的键创建了一个 table,其中包含已处理文档中的所有 <address> 元素,以其 <street> 子元素的值作为键。这意味着,如果您有这些元素:

<address>
  <street>Main Street</street>
  <number>123</number>
</address>
<address>
  <street>Main Street</street>
  <number>456</number>
</address>
<address>
  <street>Country Road</street>
  <street>Country Rd.</street>
  <number>789</number>
</address>

…然后您可以使用 key('addressByStreet', 'Main Street') 检索 Main Street 中列出的所有地址。

您可以使用 key('addressByStreet', 'Country Road')key('addressByStreet', 'Country Rd.') 来检索最后一个地址。

这里为什么要用钥匙?上面的表达式可以像 //address[street='Main Street'] 一样重新实现,但是现在每次调用这个表达式时,XSLT 处理器可能会再次遍历整个文档。如果经常调用模板或循环,就会出现问题。键可以带来巨大的性能优势(例如,将复杂度从 O(n²) 降低到 O(n)),因为结果是 "cached".

有许多应用程序和模式都使用了密钥。例如,如果你有这个 XML:

<street-list>
  <street>Main Street</street>
  <street>Bumpy Road</street>
</street-list>

表达式 street-list/street[not(key('addressByStreet', .))] 将过滤街道列表,并且只有 return 条街道在上面的列表中没有地址 - 即在这种情况下只有 "Bumpy Road" 因为对于 "Main Street",存在一个关键条目。

XSLT 1 中键的典型应用是Muenchian grouping

我现在有了用例。不,它不是遗留代码。从密钥和数据的上下文和定义中可以清楚地看出这一点。

如果我们有这样的数据:

<xsl:variable name="dict">
  <ITEMS>
    <ITEM id="1" content="it1">
      <ITEM-REF ref="3"/>
    </ITEM>
    <ITEM id="2" content="it2">
      <ITEM-REF ref="1"/>
    </ITEM>
    <ITEM id="3" content="it3">
      <ITEM-REF ref="6"/>
    </ITEM>
    <ITEM id="4" content="it4">
      <ITEM-REF ref="3"/>
    </ITEM>
    <ITEM id="5" content="it5">
      <ITEM-REF ref="5"/>
    </ITEM>
    <ITEM id="6" content="it6">
      <ITEM-REF ref="8"/>
    </ITEM>
    <ITEM id="7" content="it7">
      <ITEM-REF ref="9"/>
    </ITEM>
  </ITEMS>
</xsl:variable>

并且我们想要获得所有具有 @ref 值的 ITEM-REF 元素,其中没有 ITEM 具有相同 @id 值( 断开的链接) 表达式可以帮忙:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" encoding="utf-8" indent="yes"/>

    <xsl:variable name="dict">
      <ITEMS>
        <ITEM id="1" content="it1">
          <ITEM-REF ref="3"/>
        </ITEM>
        <ITEM id="2" content="it2">
          <ITEM-REF ref="1"/>
        </ITEM>
        <ITEM id="3" content="it3">
          <ITEM-REF ref="6"/>
        </ITEM>
        <ITEM id="4" content="it4">
          <ITEM-REF ref="3"/>
        </ITEM>
        <ITEM id="5" content="it5">
          <ITEM-REF ref="5"/>
        </ITEM>
        <ITEM id="6" content="it6">
          <ITEM-REF ref="8"/>
        </ITEM>
        <ITEM id="7" content="it7">
          <ITEM-REF ref="9"/>
        </ITEM>
      </ITEMS>
    </xsl:variable>

    <xsl:key name="itemkey" match="ITEM" use="@id"/>

    <xsl:template match="START">
      <xsl:variable name="allItems" select="msxsl:node-set($dict)//ITEM"/>
      <xsl:variable name="allItemRefs" select="msxsl:node-set($dict)//ITEM-REF"/>
      <xsl:variable name="itemRefsNotReferencingOtherItems" select="$allItemRefs[not(key('itemkey',@ref))]"/>
      <REFERENCED-NOT-EXISTING>
        <xsl:for-each select="msxsl:node-set($itemRefsNotReferencingOtherItems)">
          <ITEM>
            <xsl:attribute name="id">
              <xsl:value-of select="@ref"/>
            </xsl:attribute>
          </ITEM>
        </xsl:for-each>
      </REFERENCED-NOT-EXISTING>
    </xsl:template>

  </xsl:stylesheet>

输出:

<?xml version="1.0" encoding="utf-8"?>
<REFERENCED-NOT-EXISTING>
  <ITEM id="8" />
  <ITEM id="9" />
</REFERENCED-NOT-EXISTING>

输入文件:

<?xml version="1.0" encoding="utf-8"?>
<START/>