XSLT 2.0 - 如何找到不匹配的引用/对

时间:2017-05-30 23:45:06

标签: xml xslt

我有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;
&#13;
&#13;

在这个例子中,我希望输出返回昵称引用xyz和章节引用999,因为它们与XML文档中的任何内容都不匹配。我不需要或希望它返回有关不匹配的人或文档的任何信息。我已经尝试了20种比较序列的方法,但我必须遗漏一些东西,因为我无法得到我需要的结果。

谢谢!

2 个答案:

答案 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()">