1

我见过这个问题,其中 Dimitre Novatchev 展示了一种ends-with使用 XPath 1.0 表达式进行复制的方法。但是,我无法在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);

它返回了我想要的所有节点加上一个我没有的节点,最后有一个额外的“s”(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>
4

2 回答 2

2

如果要避免命名空间前缀,则必须使用 local-name() 函数。像这样的东西应该给你所有以 -notification 结尾的节点

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

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

http://www.xpathtester.com/test

输入

 <?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 中的子字符串函数

子字符串(本地名称(),字符串长度(本地名称())-字符串长度('-通知')+ 1,字符串长度(本地名称()))

RHS 是你搜索字符串 = '-notification'

local-name() = 给出您正在评估的节点的名称,不包括前缀 string-length() - 节点名称的长度

于 2012-08-10T14:44:05.570 回答
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>
于 2012-08-10T14:20:02.177 回答