Xpath - 节点序列的数字比较失败,仅使用第一个节点

时间:2013-04-03 17:10:56

标签: c# xpath

我发现在Xpath表达式中使用“number”函数时,该函数只使用序列中的第一个节点。问题的一个例子:

using System.Xml;
using System;
namespace ConsoleApplication1
{
    public class Program
    {
        public static void Main()
        {
            XmlDocument doc = new XmlDocument();            
            doc.LoadXml(
@"<baseball>
    <games>
        <game>
            <id>1</id>
            <stadiumId>7</stadiumId>
            <attendance>4999</attendance>
        </game>
        <game>
            <id>2</id>
            <stadiumId>5</stadiumId>
            <attendance>600</attendance>
        </game>
        <game>
            <id>3</id>
            <stadiumId>5</stadiumId>
            <attendance>789</attendance>
        </game>
    </games>
    <stadiums>
        <stadium><id>5</id><capacity>1000</capacity></stadium>
        <stadium><id>7</id><capacity>5000</capacity></stadium>
        <stadium><id>9</id><capacity>560</capacity></stadium>
    </stadiums>
</baseball>");

            printNodes("what stadiums had games occur in them?",
                doc.SelectNodes("/baseball/stadiums/stadium[./id/text()=/baseball/games/game/stadiumId/text()]"));

            printNodes("what stadiums had games occur in them? this time converting to numbers before doing comparison",
                doc.SelectNodes("/baseball/stadiums/stadium[number(./id/text())=number(/baseball/games/game/stadiumId/text())]"));
        }//Main

        private static void printNodes(string description, XmlNodeList nodeList)
        {
            Console.WriteLine("-----" + description + "-------");
            foreach (XmlNode node in nodeList)
                Console.WriteLine(node.OuterXml);
            Console.WriteLine("-------------------");
        }       
    }//class
}//namespace

输出结果为:

-----what stadiums had games occur in them?-------
<stadium><id>5</id><capacity>1000</capacity></stadium>
<stadium><id>7</id><capacity>5000</capacity></stadium>
-------------------
-----what stadiums had games occur in them? this time converting to numbers before doing comparison-------
<stadium><id>7</id><capacity>5000</capacity></stadium>
-------------------

它仅在第二个输出中返回体育场7的原因(尽管Xpath查询在逻辑上相同)是因为第一个棒球/游戏/游戏节点的stadiumId为7.没有其他游戏节点被评估。

这是数字功能的预期功能吗?有办法解决这个问题吗?

2 个答案:

答案 0 :(得分:2)

首先:设计number仅转换节点集中的第一个元素 - 请参阅number in XPath specification

第二: XPath中的=更像是“任何一对匹配”,而不是“双方都相等”。所以你的第一个陈述是正确的,因为“游戏/游戏”中的元素与“id”匹配“体育场”,第二个不能按预期工作,因为“游戏/游戏/列表” stadiumId“由”number“函数转换的节点变为列表中第一个的ID(见上文)。

如果您要编写许多XPath表达式,请阅读规范,至少是定义“=”如何工作的Booleans部分。

答案 1 :(得分:2)

如果你真的想比较数字,可以在XPath 2.0中使用

  /*/stadiums/*
        [xs:integer(id) = /*/games/*/stadiumId/xs:integer(.)]

基于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 omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
  <xsl:sequence select=
  "/*/stadiums/*
         [xs:integer(id) = /*/games/*/stadiumId/xs:integer(.)]"/>
 </xsl:template>
</xsl:stylesheet>

在提供的XML文档上应用此转换时:

<baseball>
    <games>
        <game>
            <id>1</id>
            <stadiumId>7</stadiumId>
            <attendance>4999</attendance>
        </game>
        <game>
            <id>2</id>
            <stadiumId>5</stadiumId>
            <attendance>600</attendance>
        </game>
        <game>
            <id>3</id>
            <stadiumId>5</stadiumId>
            <attendance>789</attendance>
        </game>
    </games>
    <stadiums>
        <stadium>
            <id>5</id>
            <capacity>1000</capacity>
        </stadium>
        <stadium>
            <id>7</id>
            <capacity>5000</capacity>
        </stadium>
        <stadium>
            <id>9</id>
            <capacity>560</capacity>
        </stadium>
    </stadiums>
</baseball>

评估XPath表达式并将此评估结果(所选元素)复制到输出

<stadium>
            <id>5</id>
            <capacity>1000</capacity>
</stadium>
<stadium>
            <id>7</id>
            <capacity>5000</capacity>
</stadium>