在SelectNodes中使用XPath表达式

时间:2012-08-10 13:59:01

标签: c# .net xpath-1.0

I've seen this question Dimitre Novatchev展示了一种使用XPath 1.0表达式复制ends-with的方法。但是,我在SelectNodes调用的情况下无法实现它。

以前我在使用

XmlElement root = doc.DocumentElement;
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("x", root.NamespaceURI);    
XmlNodeList nodeList = doc.SelectNodes("//x:*[contains(name(.), '-notification')]", nsmgr);

返回了我想要的所有节点加上我没有返回的节点(has-more-notifications)。

所以我尝试使用Dimitre表达式给了我:

XmlNodeList nodeList = doc.SelectNodes("//x:*[substring(name(.), string-length(name(.)) - string-length('-notification') +1)]", nsmgr);

哪个失败了,给了我notification-data-response的根节点。

这是我第一次涉足XPath,它似​​乎就像正则表达式 - 你要么理解它,要么你不理解它。

如何实现表达式,使其仅返回以-notification结尾的节点?

更新

输入样本:

<?xml version="1.0" encoding="UTF-8"?>
<notification-data-response xmlns="http://checkout.google.com/schema/2" serial-number="16ceae10-a9f1-4ff0-a77b-c3407f2d684a">
    <notifications>
        <new-order-notification serial-number="653417067275702-00001-7">
        </new-order-notification>
        <order-state-change-notification serial-number="653417067275702-00005-1">
        </order-state-change-notification>
        <risk-information-notification serial-number="653417067275702-00005-5">
        </risk-information-notification>
        <authorization-amount-notification serial-number="653417067275702-00005-6">
        </authorization-amount-notification>
    </notifications>
    <continue-token>CP6u9NeQJxC2y72h-MiUARgG</continue-token>
    <has-more-notifications>false</has-more-notifications>
</notification-data-response>

2 个答案:

答案 0 :(得分:2)

如果要避免使用名称空间前缀,则必须使用local-name()函数。这样的事情应该为你提供以-notification

结尾的所有节点
  

// node()[substring(local-name(),string-length(local-name()) -   string-length(' - notification')+ 1,string-length(local-name()))=   '-notification']

好的..我在这里测试了这个。您也可以验证.XPath是一个开放标准,因此所有工具都应该能够给出类似的响应。

http://www.xpathtester.com/test

INPUT

 <?xml version="1.0"?>
<notification-data-response xmlns:x="test">
    <TPI_ADDRESSES>
        <ISPRIMARY>Y</ISPRIMARY>
        <data1>
            <x:wewe-notification/>
        </data1>
        <data2>
            <x:wewe-notification/>
        </data2>
    </TPI_ADDRESSES>
</notification-data-response>

输出

<?xml version="1.0" encoding="UTF-8"?>

<root>
  <x:wewe-notification xmlns:x="test"/>
  <x:wewe-notification xmlns:x="test"/>
</root>

忽略根节点,因为生成它以呈现有效的可形成响应。

现在让我分解并向你解释xpath:

  

“//” - 表示您正在搜索狂野搜索或我们称之为完全扫描...就像您没有   关心目标节点到达什么级别。

     

“node()” - 是对任何节点或当前节点的引用....

所以现在: “// node()” - 一起表示你将评估xml中的所有节点。

你评价什么?

名称 - 您想要查找节点的名称中是否包含“-notification”。为此,您使用LHS中的子字符串函数

  

substring(local-name(),string-length(local-name()) -   string-length(' - notification')+ 1,string-length(local-name()))

     

RHS是你搜索string =' - notification'

local-name()=给出您在除前缀之外的任何点评估的节点的名称 string-length() - 节点名称的长度

答案 1 :(得分:0)

Dimitre的解决方法效果很好。您只需要将子结构的“结束”与您要查找的字符串常量进行比较,即:

SelectNodes("//x:*['-notification'=substring(name(.), string-length(name(.)) 
           - string-length('-notification') +1)]")

出于兴趣,如果您可以通过更具体的路径来避免//通配符,那么您将在大型文档上显着提高查询的性能。

修改

以下是我测试的方法(在MS Visual Studio的xslt解析器中,为1.0):

<root>
    <nodeendsin-notification></nodeendsin-notification>
    <alsonodeendsin-notification></alsonodeendsin-notification>
    <nopethisis-notifications></nopethisis-notifications>
    <idontcontainatall></idontcontainatall>
</root>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/root">
        <root>
            <xsl:apply-templates/>
        </root>
    </xsl:template>
    <xsl:template match="*['-notification'=substring(name(.), string-length(name(.)) - string-length('-notification') +1)]">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="@* | node()">
    </xsl:template>
</xsl:stylesheet>

仅返回以'notification'结尾的节点

<root>
    <nodeendsin-notification></nodeendsin-notification>
    <alsonodeendsin-notification></alsonodeendsin-notification>
</root>