2

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 doc 示例时应输出:

1

这有意义吗?

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

4

2 回答 2

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

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

如果您想计算有多少/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>
于 2012-07-29T20:48:47.650 回答
1

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()功能。


二、使用单个 XPath 2.0 表达式:

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

当这个 Xpath 2.0 表达式针对同一个 XML 文档(上图)进行评估时,会产生想要的正确结果

 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:ifxsl:when

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

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

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

number(true()) = 1

number(false()) = 0
于 2012-07-29T21:07:14.200 回答