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

