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/>
我读过这样的表达方式
<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/>