我有XML文档,其中包含许多通过id / ref对引用其他元素的元素,类似于下面显示的示例。我只需要确定是否有任何引用与任何现有ID都不匹配。我认为这很简单,但如果确实如此,我就错过了它的诀窍。
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Person id="abc">
<Name>David</Name>
</Person>
<Person id="def">
<Name>Mark</Name>
</Person>
<Person id="ghi">
<Name>James</Name>
</Person>
<Nickname ref="ghi">
<Nname>Jim</Nname>
</Nickname>
<Nickname ref="abc">
<Nname>Dave</Nname>
</Nickname>
<Nickname ref="xyz">
<Nname>Fred</Nname>
</Nickname>
<Document id="123">
<DocName>Document 1</DocName>
</Document>
<Document id="456">
<DocName>Document 2</DocName>
</Document>
<Chapter ref="123">
<Cname>Chapter 1</Cname>
</Chapter>
<Chapter ref="999">
<Cname>Chapter 999</Cname>
</Chapter>
</Root>
&#13;
在这个例子中,我希望输出返回昵称引用xyz和章节引用999,因为它们与XML文档中的任何内容都不匹配。我不需要或希望它返回有关不匹配的人或文档的任何信息。我已经尝试了20种比较序列的方法,但我必须遗漏一些东西,因为我无法得到我需要的结果。
谢谢!
答案 0 :(得分:0)
这是您可以使用的一种相当简单的方法:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="elem-by-id" match="*" use="@id" />
<xsl:template match="/Root">
<unmatched-references>
<xsl:for-each select="*[@ref][not(key('elem-by-id', @ref))]">
<xsl:copy>
<xsl:copy-of select="@ref"/>
</xsl:copy>
</xsl:for-each>
</unmatched-references>
</xsl:template>
</xsl:stylesheet>
处理输入示例的结果将是:
<?xml version="1.0" encoding="UTF-8"?>
<unmatched-references>
<Nickname ref="xyz"/>
<Chapter ref="999"/>
</unmatched-references>
由于似乎对xsl:for-each
在上述例子中的作用存在一些误解,让我再示一个:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="elem-by-id" match="*" use="@id" />
<xsl:template match="/Root">
<xsl:variable name="unmatched-referring-elements" select="*[@ref][not(key('elem-by-id', @ref))]"/>
<unmatched-references>
<xsl:value-of select="$unmatched-referring-elements/@ref" separator=", "/>
</unmatched-references>
</xsl:template>
</xsl:stylesheet>
<强>结果强>
<?xml version="1.0" encoding="UTF-8"?>
<unmatched-references>xyz, 999</unmatched-references>
答案 1 :(得分:0)
正如我在评论中提到的,我在另一个站点上发现了这个,“XPath只允许对节点序列进行集合操作。不允许使用原子值。这是因为set操作超过了节点标识而不是值。“我在这里为那些可能看到这个页面的人重复一遍,但没有阅读所有的评论。
这意味着我的初始基于集合的方法永远不会起作用,因为我想要使用的集合由值组成,而不是节点。所以我使用密钥切换到基于模板的方法。
所以,我正在使用一个密钥,就像这样;
<xsl:key name="elem-by-id" match="*" use="@id" />
除了那个键,我创建了一个模板,其工作方式与Michael的变量相同。这会将处理限制为那些@ref与XML文档中的任何@id都不匹配的元素。
<xsl:template match="//*[@ref][not(key('elem-by-id', @ref))]">
直接获取包含不匹配的@ref属性的元素。然后,在模板内部,我可以通过非常简单的选择获得不匹配的@ref的值。
<xsl:value-of select="@ref"/>
我选择使用这种方法,因为它还可以让我轻松检索包含不匹配的@ref的元素的名称。
<xsl:value-of select="./../name()">