使用XSLT键查找唯一值

5
<ROOT>
<AA Aattr="xyz1">
    <BB bAttr1="firefox"  bAttr2="aaa" >
    </BB>   
    <BB bAttr1="chrome"   bAttr2="aaa" >
    </BB>
    <BB bAttr1="firefox"  bAttr2="bbb" >
    </BB>
    <BB bAttr1="chrome"   bAttr2="bbb" >
    </BB>
</AA>
<AA Aattr="xyz2">
    <BB bAttr1="firefox"  bAttr2="aaa" >
    </BB>   
    <BB bAttr1="chrome"   bAttr2="ccc" >
    </BB>
    <BB bAttr1="firefox"  bAttr2="ddd" >
    </BB>
</AA>

我希望从节点“AA”中选择属性“Aattr”为“xyz1”的节点,并选择节点“BB”中属性“bAttr2”的不同/唯一值。

例如对于给定的XML,我需要输出“aaa”,“bbb”。

我尝试使用键来实现上述逻辑,但未成功。请帮忙解决。

<xsl:key name="nameDistinct" match="BB" use="@bAttr1"/>
<xsl:template match="/">
<xsl:for-each select="ROOT/AA[@Aattr='xyz1']">
    <xsl:for-each select="BB[generate-id()=generate-id(key('nameDistinct',@bAttr2)[1])]">
    <xsl:value-of select="@bAttr2"/>            
    </xsl:for-each>
</xsl:for-each>
</xsl:template>
2个回答

5
你有两个选择:
1. 在定义键时,过滤可用的项: 2. 在使用键时,校验并过滤所需的项:
  <xsl:key name="nameDistinct" match="AA[@Aattr = 'xyz1']/BB" use="@bAttr2"/>

  <xsl:template match="/">
    <xsl:for-each 
      select="ROOT/AA/BB[generate-id() =
                         generate-id(key('nameDistinct', @bAttr2)[1])]">
      <xsl:value-of select="@bAttr2"/>
    </xsl:for-each>
  </xsl:template>

或在分组表达式内进行筛选:

  <xsl:key name="nameDistinct" match="BB" use="@bAttr2"/>

  <xsl:template match="/">
    <xsl:for-each 
      select="ROOT/AA/BB[generate-id() =
                         generate-id(key('nameDistinct', @bAttr2)
                                       [../@Aattr = 'xyz1']
                                       [1])]">
      <xsl:value-of select="@bAttr2"/>
    </xsl:for-each>
  </xsl:template>

前一种方法稍微不那么混乱,效率略高,而后一种方法允许您对分组进行参数化(即根据非硬编码为“xyz1”的值进行分组),例如:

  <xsl:key name="nameDistinct" match="BB" use="@bAttr2"/>

  <xsl:template match="/">
    <xsl:for-each select="ROOT/AA">
      <xsl:for-each
        select="BB[generate-id() =
                   generate-id(key('nameDistinct', @bAttr2)
                                    [../@Aattr = current()/@Aattr]
                                    [1])]">
        <xsl:value-of select="@bAttr2"/>
      </xsl:for-each>
    </xsl:for-each>
  </xsl:template>

2

这种转换

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 
 <xsl:key name="kCompoundAttribs" match="BB"
  use="concat(generate-id(..), '+', @bAttr2)"/>
 
 <xsl:template match="/">
  <xsl:copy-of select=
   "/*/*[1]/*
     [generate-id()
     =
      generate-id(key('kCompoundAttribs',
                      concat(generate-id(..),'+', @bAttr2)
                      )[1]
                  )
     ]"/>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档时:

<ROOT>
    <AA Aattr="xyz1">
        <BB bAttr1="firefox"  bAttr2="aaa" ></BB>
        <BB bAttr1="chrome"   bAttr2="aaa" ></BB>
        <BB bAttr1="firefox"  bAttr2="bbb" ></BB>
        <BB bAttr1="chrome"   bAttr2="bbb" ></BB>
    </AA>
    <AA Aattr="xyz2">
        <BB bAttr1="firefox"  bAttr2="aaa" ></BB>
        <BB bAttr1="chrome"   bAttr2="ccc" ></BB>
        <BB bAttr1="firefox"  bAttr2="ddd" ></BB>
    </AA>
</ROOT>

生成两个BB元素,其bAttr2属性具有所需的不同值集合(如果您只需要属性的字符串值,请使用xsl:apply-templatesxsl:for-each表达式):

<BB bAttr1="firefox" bAttr2="aaa"/>
<BB bAttr1="firefox" bAttr2="bbb"/>

请注意:

这个解决方案比“过滤”更简单易懂,更高效。


请注意,"xml"是<xsl:output>声明的@method属性的默认值。这意味着如果它是“xml”,我们根本不需要指定@method属性。 - Dimitre Novatchev
嗯,这并不像那么简单。规范说:_方法属性的默认值取决于要序列化的树的内容_(这是规范2.0,但在1.x中也是一样)。但实际上,在这种情况下,它应该是xml。然而,当我在Altova XMLSpy中运行它时,如果我没有指定方法,它会输出HTML。可能是XMLSpy中的一个错误。无论如何,在我看来,我建议添加输出方法以兼容所有环境。 - Adrian W
@AdrianW 我强烈建议你联系Altova。也许你没有使用他们最新的版本,这个问题可能已经被修复了,或者他们需要知道这个问题。在过去的20年里,你的问题是我注意到的唯一一个这种类型的问题。 - Dimitre Novatchev
@AdrianW,规范(https://www.w3.org/TR/2021/REC-xslt20-20210330/#element-output)指出(第2条),仅当默认值设置为“html”时的唯一情况是:“如果此第一个元素子节点的扩展QName的局部部分为html(大小写任意)且空命名空间URI,则默认输出方法为html”。 如果这不是您的情况,则XML Spy中显然存在错误。 请注意,本答案提供的XML文档的转换明显不满足项目条件,因此输出方法将为“xml”。 - Dimitre Novatchev
使用最新的XMLSpy 2023。所以,这不是问题。我现在看到程序选项中有一个标记为“默认文件扩展名”的设置,它默认设置为.html(至少我不能记得曾经触及过此设置)。这隐含地确定了输出方法的默认值。因此,通过将默认扩展名设置为.xml,可以使XMLSpy符合标准。 - Adrian W
@AdrianW +++ 1000000 :) - Dimitre Novatchev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接