XPath - 选择具有一个相似属性和一个不同属性的节点

时间:2012-08-11 15:31:30

标签: xslt xpath

所以我无法理解为什么一个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" />

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

<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" /> 

2 个答案:

答案 0 :(得分:3)

<强>予。这种转变:

<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文档(添加到提供的XML文档中的其他source元素 - 验证更多案例):

<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"/>

<强> II。使用一个密钥的解决方案:

<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"/>

<强> III。解决方案有两个键:

<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"/>

答案 1 :(得分:1)

source[key('destId', @id) and not(key('destLevel', @level))]

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

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

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

set:difference()来自EXSLT。