0

所以我很难理解为什么一个 XPath 表达式得到我想要的节点,而另一个没有。

首先,xml:

<doc>
    <source id="225" clientID="567" matterID="225" level="2" />
    <source id="226" clientID="993" matterID="226" level="2" />
    <dest id="185" level="7" />
    <dest id="226" level="7" />
</doc>

我的 xsl 模板中的键定义如下:

<xsl:key name="sourceId" match="//source" use="@id" />
<xsl:key name="destId" match="//dest" use="@id" />
<xsl:key name="destLevel" match="//dest" use="@level" />

我正在寻找的是源节点,它与 id 上的 dest 节点匹配,但具有不同的 level 属性。我认为可以在我脑海中使用的应用模板如下:

<xsl:apply-templates select="source[key('destId', @id) and not(key('destLevel', @level))]" mode="update" />

但这似乎不起作用。一位同事建议在与我不想要的节点匹配的表达式周围放置一个 not ,经过大量试验和错误,我认为这可能有效,但没有效果:

<xsl:apply-templates select="source[not(not(key('destId', @id)) or not(key('destLevel', @level)))]" mode="update" />

谁能告诉我我需要什么来解决这个问题?

编辑:我以前以为我已经用第二个查询解决了这个问题,但似乎我错了。

====解决方案====

Dimitre Novatchev 详细介绍了解决此问题的不同方法,但我的最终解决方案实际上与他的略有不同。

本质上,我使用结合了这两个属性的 concat() 函数创建了一个虚拟键。这样,我可以找到与 id 匹配的节点,但不是 id 级别的组合。

额外的钥匙:

<xsl:key name="destByIdAndLevel" match="//dest" use="concat(@id,'+',@level)" />

更改了应用模板调用:

<xsl:apply-templates select="source[key('destId', @id) and not(key('destByIdAndLevel',concat(@id,'+',@level)))]" mode="update" /> 
4

2 回答 2

3

I. 这种转变

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

 <xsl:template match="source">
   <xsl:copy-of select=
     "self::*[../dest[@id = current()/@id and not(@level=current()/@level)]]"/>
 </xsl:template>
</xsl:stylesheet>

应用于此 XML 文档时source(添加到提供的 XML 文档的附加元素——以验证更多案例):

<doc>
 <source id="185" clientID="567" matterID="225" level="7" />
 <source id="225" clientID="567" matterID="225" level="2" />
 <source id="226" clientID="993" matterID="226" level="2" />
 <dest id="185" level="7" />
 <dest id="226" level="7" />
</doc>

产生想要的正确结果

<source id="226" clientID="993" matterID="226" level="2"/>

二、使用一键解决方案

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kDestById" match="dest" use="@id"/>

 <xsl:template match="/*">
  <xsl:copy-of select=
   "source[key('kDestById',@id)
         and
           not(@level=key('kDestById',@id)/@level)]"/>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于同一个 XML 文档(上图)时,同样会产生相同的正确结果

<source id="226" clientID="993" matterID="226" level="2"/>

三、有两个键的解决方案

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kDestById" match="dest" use="@id"/>
 <xsl:key name="kDestByLevel" match="dest" use="@level"/>

 <xsl:template match="source">
  <xsl:copy-of select=
   "self::*
        [key('kDestById',@id)
       and
         key('kDestById',@id)
             [not(count(.|key('kDestByLevel',current()/@level))
                 =
                  count(key('kDestByLevel',current()/@level))
                 )
             ]
        ]"/>
 </xsl:template>
</xsl:stylesheet>

这再次产生了想要的正确结果

 <source id="226" clientID="993" matterID="226" level="2"/>
于 2012-08-11T18:01:37.967 回答
1
source[key('destId', @id) and not(key('destLevel', @level))]

这使得源节点的 id 与某些 dest 节点相同,并且具有与任何 dest 节点不同的级别。请注意,它不需要特定的dest 节点具有相同的 id 但不同的级别。

我想你正在寻找这样的东西:

source[set:difference(key('destId', @id), key('destLevel', @level))]

set:difference()来自 EXSLT。

于 2012-08-11T18:00:59.410 回答