检查输出中的重复元素

时间:2020-03-06 14:58:36  来源:igfitidea点击:

我有一些XML,例如,它看起来像这样:

<root>
    <field1>test</field1>
    <f2>t2</f2>
    <f2>t3</f2>
</root>

我想用XSLT对其进行转换,但是我想抑制输出中的第二个f2元素,当处理源中的第二个f2元素时,如何在模板内部检查输出中是否已经存在f2元素?我的XSLT目前看起来像这样:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="no" omit-xml-declaration="yes" standalone="no" />
  <xsl:template match="/">
    <xsl:for-each select="./root">
      <output>
        <xsl:apply-templates />        
      </output>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="*" >
      <xsl:element name="{name(.)}">
        <xsl:value-of select="." />
      </xsl:element>
  </xsl:template>
</xsl:stylesheet>

我需要围绕模板中的xsl:element进行某种检查,但是我不确定如何查询输出文档以查看元素是否已存在。

编辑:忘记了pre标签,代码现在应该可见了!

解决方案

这取决于我们要达到的系统范围。

也就是说,我们是否只关注属于同一父元素的子元素,或者是同一级别的所有元素(如果愿意,可以使用'cousins')或者文档中任何位置的元素...

在第一种情况下,我们可以检查前一个同级轴,以查看是否存在其他具有相同名称的元素。

<xsl:if test="count(preceding-sibling::node()[name()=name(current())])=0">
  ... do stuff in here.
</xsl:if>

仅检查(并警告我们有重复),我们可以在此处找到示例

类似于以下内容:

<xsl:for-each-group select="collection(...)//@id" group-by=".">
  <xsl:if test="count(current-group()) ne 1">
    <xsl:message>Id value <xsl:value-of select="current-grouping-key()"/> is 
       duplicated in files
       <xsl:value-of select="current-group()/document-uri(/)" separator=" and
    "/></xsl:message>
  </xsl:if>
 </xsl:for-each-group>

要修改以选择"根"元素内的所有节点。

至于删除重复的行,我们在这里还有另一个示例

看起来像:

<xsl:stylesheet>
  <xsl:key name="xyz" match="record[x/y/z]" use="x/y/z" />
  <xsl:variable name="noxyzdups" select="/path/to/record[generate-id(.) = generate-id(key('xyz', x/y/z))]" />
...
  <xsl:template ... >
    <xsl:copy-of "exslt:node-set($noxyzdups)" />
  </xsl:template>
</xsl:stylesheet>

x / y / z是我们要使其唯一的xpath表达式。它可以是concat(x,'-',@ y,'-',z)或者任何我们想要的。

现在,我不确定这两个示例是否可以轻松地适应情况,但我只是想指出这两个来源,以防万一。

无法询问转换的输出。也无法跟踪转换的当前状态(即跟踪变量中已发出的节点)。从根本上讲,这不是XSLT的工作方式。无副作用编程环境的代价之一是,我们不能做有副作用的事情。那好吧。

在情况下,完成此操作的一种方法是构建一个包含所有源元素列表的变量,这些源元素可以转换为只希望发出一次的输出元素。然后对照此列表检查要转换的每个节点。如果不在列表中,则发出它。如果它是列表中的第一项,请发出它。否则,请不要。