xml 如何选择唯一节点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/227711/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 12:10:21  来源:igfitidea点击:

How to select unique nodes

xmlxslt

提问by pc1oad1etter

I found this pagedescribing the Muenchian method, but I think I'm applying it wrong.

我发现这个页面描述了 Muenchian 方法,但我认为我应用它是错误的。

Consider that this would return a set of ages:

考虑这将返回一组年龄:

/doc/class/person/descriptive[(@name='age')]/value

1..2..2..2..3..3..4..7

1..2..2..2..3..3..4..7

But I would like a nodeset only one node for each age.

但我想要一个节点集,每个年龄只有一个节点。

1..2..3..4..7

1..2..3..4..7

Each of these seem to return all of the values, instead of unique values:

其中每一个似乎都返回所有值,而不是唯一值:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::value)]/value
/doc/class/person/descriptive[(@name='age')]/value[not(value=preceding-sibling::value)]

What am I missing?

我错过了什么?

回答by BQ.

Here's an example:

下面是一个例子:

<root>
    <item type='test'>A</item>
    <item type='test'>B</item>
    <item type='test'>C</item>
    <item type='test'>A</item>
    <item type='other'>A</item>
    <item type='test'>B</item>
    <item type='other'>D</item>
    <item type=''>A</item>
</root>

And the XPath:

和 XPath:

//preceding::item/preceding::item[not(.=preceding-sibling::item)]/text()

Results: A B C D

结果:ABCD

EDIT: As mousio commented this doesn't capture the last item in a list if it's the only time it appears. Taking that and F?anor's comment into account, here's a better solution:

编辑:正如 mousio 评论的那样,如果它是唯一出现的时间,它不会捕获列表中的最后一个项目。考虑到这一点和 F?anor 的评论,这是一个更好的解决方案:

/root/item[not(.=preceding-sibling::item)]

回答by ChuckB

Here is the Muenchian version of BQ's answer using his data:

这是使用他的数据的 BQ 答案的 Muenchian 版本:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes" method="text"/>
  <xsl:key name="item-by-value" match="item" use="."/>

  <xsl:template match="/">
    <xsl:apply-templates select="/root/item"/>
  </xsl:template>

  <xsl:template match="item">
    <xsl:if test="generate-id() = generate-id(key('item-by-value', normalize-space(.)))">
      <xsl:value-of select="."/>
      <xsl:text>
</xsl:text>
    </xsl:if>
  </xsl:template>

  <xsl:template match="text()">
    <xsl:apply-templates/>
  </xsl:template>
</xsl:stylesheet>

This transform gives

这种变换给出

A
B
C
D



Ç
d

  1. The key()lookup above in the template for itemreturns a nodeset containing all the itemelements with the same string value as the context node.
  2. If you apply a function that expects a single node to a nodeset, it will operate on the first node in that nodeset.
  3. All calls to generate-id()are guaranteed to generate the same ID for a given node during a single pass through a document.
  4. Therefore, the test will be true if the context node is the same node as the first one returned by the key()call.
  1. key()上面在模板中的查找item返回一个节点集,其中包含item与上下文节点具有相同字符串值的所有元素。
  2. 如果将需要单个节点的函数应用于节点集中,它将在该节点集中的第一个节点上运行。
  3. generate-id()在单次遍历文档期间,所有对 的调用都保证为给定节点生成相同的 ID。
  4. 因此,如果上下文节点与key()调用返回的第一个节点相同,则测试为真。

回答by Grégory

For those who still look for a select distinct in XSLT:

对于那些仍然在 XSLT 中寻找不同选择的人:

With XSLT 2.0, you can use "distinct-values(/doc/class/person/descriptive[(@name='age')]/value)"

使用 XSLT 2.0,您可以使用“distinct-values(/doc/class/person/descriptive[(@name='age')]/value)”

回答by matpie

The Muenchian method uses keys to create a unique list of items from the node set. For your data, the key would look like this:

Muenchian 方法使用键从节点集中创建一个唯一的项目列表。对于您的数据,密钥如下所示:

<!-- Set the name to whatever you want -->
<xsl:key name="PeopleAges" match="/doc/class/person/descriptive[@name = 'age']/value" use="." />

From there, I would personally use xsl:apply-templatesbut you can use the following selectattribute in other places:

从那里,我会亲自使用,xsl:apply-templates但您可以select在其他地方使用以下属性:

<!-- you can change `apply-templates` to: `copy-of` or `for-each`. -->
<xsl:apply-templates select="/doc/class/person/descriptive[@name = 'age']/value[count(. | key('PeopleAges', .)[1]) = 1]" />

The accompanying match for the above is much simpler:

上面的伴随匹配要简单得多:

<xsl:template match="person/descriptive[@name = 'age']/value">
    <strong>Age: </strong><xsl:value-of select="." />
</xsl:template>

回答by JacobE

Aren't you missing a reference to 'descriptive' right after the preceding-value? Some thing like the following:

您不是在前面的值之后缺少对“描述性”的引用吗?像下面这样的事情:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::descriptive[@name='age']/value)]/value

(Haven't tested it)

(没测试过)