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



XML/XSL/Xpath的新手,我已经在这些板上搜索了一个看似简单的问题的答案,但也许我对XSL太陌生,无法理解一些可能解决这个问题的答案。

我想选择一组与存储在文档中其他位置的不同节点值集匹配的节点,以便:

<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>

因此,假设我想计算所有"部分",但我不知道"部分"节点名称,我希望 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 文档示例时,上述内容应输出:

1

这有意义吗?

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

您可以使用

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 规范第 3.4 节:

如果要比较的一个对象是节点集,另一个对象是字符串, 那么当且仅当 节点集使得在 节点和其他字符串的字符串值为 true。

如果要计算/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>

I.此 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元素的字符串值之一。

上述表达式可以在任何需要布尔值的地方"按原样"使用(例如在任何谓词中,作为xsl:ifxsl:when指令的test属性的值,...等。

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

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

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

number(true()) = 1

number(false()) = 0

最新更新