XSL问题:如何根据一组节点值选择一组节点?

时间:2012-07-29 20:13:01

标签: xml xslt xpath

XML / XSL / Xpath的新手,我已经仔细研究了这些主板以寻找一个看似简单的问题的答案,但也许我对XSL来说太新了,无法理解那些可能的答案。解决这个问题

我想选择一组与doc中其他地方存储的不同节点值匹配的节点,以便:

<body>
    <partlist>
        <part>head</part>
        <part>spleen</part>
        <part>toe</part>
    </partlist>
    <parts>
        <head>this is a head</head>
        <spleen>this is a spleen</spleen>
        <toe>big toe</toe>
        <toe>big toe</toe>
    <parts>
</body>

所以,让我们说我想要计算所有&#34;部分&#34;,但我不知道&#34;部分&#34;节点名称,我希望XPath expr匹配一组节点,其名称等于另一组节点的值......

我对XSL足够新,他们都认为解决方案非常简单,但经验足以让人意识到希望XSL的简单解决方案是一条肯定错误的道路。

在我基于程序的无知中,这就是我所尝试过的:

<xsl:template match="/">
    <xsl:apply-templates select="partlist/part">
        <xsl:with-param name="parts" select="parts"/>
    </xsl:apply-templates>
</xsl:template>
<xsl:template match="part">
    <xsl:param name="parts"/>
    <xsl:value-of select="count($parts[.])"/>        
</xsl:template>

但这是完全错误的。非常感谢任何帮助。

编辑:感谢您的回答,但我认为我没有被理解 - 错误是我的,因为我还没有完全清楚......我需要的是一个结果告诉我是否有任何节点与引用集的节点值相匹配...我认为会更改查询,因为我没有尝试输出计数列表,我试图匹配一个集合与一个集合设置计算总数...但是让我用XSL澄清,如果这样的事情是可能的......这里是我的代码,稍微修改以使我的意图明确:

<xsl:template match="/">
    <xsl:apply-templates select="partlist/part">
        <xsl:with-param name="parts" select="parts"/>
    </xsl:apply-templates>
</xsl:template>
<xsl:template match="part">
    <xsl:param name="parts"/>
    <xsl:if select="count($parts[.]) &gt; 0">1</xsl:if>        
</xsl:template>

以上当应用于xml doc示例时应输出:

1

这有意义吗?

编辑:我还应该指定我仅限于XSL 1.0

2 个答案:

答案 0 :(得分:1)

您可以使用local-name()函数获取节点的名称,以便将其与给定值进行比较。

例如,您的样式表可以修改为:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">
    <xsl:apply-templates select="body/partlist/part"/>
  </xsl:template>

  <xsl:template match="part">
    <xsl:variable name="part" select="."/>
    <xsl:value-of select="count(/body/parts/*[local-name() = $part])"/>
  </xsl:template>

</xsl:stylesheet>

已编辑添加:

您还可以与值集进行比较,而不仅仅是单个值。来自XPath 1.0 spec, section 3.4

  

如果要比较的一个对象是节点集而另一个是字符串,   那么当且仅当有一个节点时,比较才会成立   node-set使得执行比较的结果   节点的字符串值和另一个字符串为真。

如果您想要计算/body/parts下的本地名称与/body/partlist/part中列出的部分匹配的元素数量,您可以使用此XPath表达式执行此操作:

count(/body/parts/*[local-name() = /body/partlist/part])

编辑后添加的问题的最后一个转换可以重写,例如:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="body">
    <xsl:apply-templates select="partlist">
      <xsl:with-param name="parts" select="parts"/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="partlist">
    <xsl:param name="parts"/>
    <xsl:if test="count($parts/*[local-name() = current()/part]) &gt; 0">1</xsl:if>        
  </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

<强>予。这个XSLT 1.0转换:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:template match="part">
  <xsl:value-of select="."/>

  <xsl:value-of select=
  "concat(': ',
          count(/*/parts/*[name() = current()]),
          '&#xA;')"/>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

应用于提供的XML文档时:

<body>
    <partlist>
        <part>head</part>
        <part>spleen</part>
        <part>toe</part>
    </partlist>
    <parts>
        <head>this is a head</head>
        <spleen>this is a spleen</spleen>
        <toe>big toe</toe>
        <toe>big toe</toe>
    </parts>
</body>

生成想要的正确结果

head: 1
spleen: 1
toe: 2

<强>解释

正确使用模板和current()功能。


<强> II。使用单个XPath 2.0表达式:

   /*/partlist
       /part
          /concat(.,
                  ': ', for $p in .
                         return count(/*/parts/*[name() eq $p]),
                  '&#xA;'
                 )

当针对相同的XML文档(上面)评估此Xpath 2.0表达式时,会生成所需的正确结果

 head: 1
 spleen: 1
 toe: 2

以下是基于XSLT 2.0的验证

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output method="text"/>

 <xsl:template match="/">
  <xsl:sequence select=
  "/*/partlist
       /part
          /concat(.,
                  ': ', for $p in .
                         return count(/*/parts/*[name() eq $p]),
                  '&#xA;'
                 )
  "/>
 </xsl:template>
</xsl:stylesheet>

<强>更新

OP现在已经改变了问题,似乎他想:

  

我需要的是一个结果,告诉我是否有任何节点   匹配引用集的节点值...

这同样简单 - 只需一个XPath表达式即可获得

/*/parts/*[name() = /*/partlist/part]

true() /*/parts/*name()元素的字符串值之一时,此表达式的确切评估为/*/partlist/part

上述表达式可以在预期布尔值的任何地方“按原样”使用(例如在任何谓词中,作为test或{{xsl:if属性的值1}}指令,......等等。

如果需要值1(anf不是xsl:when),只需使用:

true()

这给了我们想要的结果1或0,因为根据定义:

number(/*/parts/*[name() = /*/partlist/part])

number(true()) = 1