【问题标题】:How to dynamically query XDocument using XPath when XML has namespaces当 XML 具有命名空间时如何使用 XPath 动态查询 XDocument
【发布时间】:2016-03-31 18:50:49
【问题描述】:

我一直在使用this absolute XPath extension 来获取XDocument 中的XPath 节点字符串。然后可以稍后通过 XPath 使用 xdoc.XPathSelectElement(xpath) 查询该节点。

但是,对于使用命名空间的 XML 文档,这会失败,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.svsxml.svs.com" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Header>
        <wsse:Security soapenv:mustUnderstand="1">
            <wsse:UsernameToken>
                <wsse:Username>Pizza</wsse:Username>
                <wsse:Password>Pie</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
</soapenv:Envelope>

Username 节点上调用GetAbsoluteXPath 给出:

/Envelope/Header[1]/Security[1]/UsernameToken[1]/Username[1]

但是,使用 XPathSelectElement 和上述 XPath 查询 XDocument 会产生 null,因为没有指定命名空间。

根据我查找的答案,解决方案是add a NamespaceManager and manually edit the path。但是,我的路径是动态生成的(我事先不知道 XML 文档的名称空间或结构),因此不能手动调整字符串。

我的问题是:

  • 有没有办法以完全忽略命名空间的方式使用 XPath 进行查询?

An answer to a similarly-named question was to query by local name instead of XPath。但是,当多个节点的名称为 LocalName(我正在解析的 XML 中经常出现)时,这会失败,因为按名称搜索只会完全抛弃 XPath 的特殊性。

澄清:我事先不知道 XML 文档是什么样的,XPath 和命名空间是在运行时确定的,而不是在编译时确定的。因此,手动添加仅适用于这个特定示例的字符串通常不会起作用。

【问题讨论】:

    标签: c# xml xpath


    【解决方案1】:

    下面的代码是 xml linq,将与一个节点一起工作。对于多个节点,您必须添加额外的搜索信息才能获得您正在寻找的确切节点。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            const string FILENAME = @"c:\temp\test.xml";
            static void Main(string[] args)
            {
                XDocument doc = XDocument.Load(FILENAME);
    
                XElement envelope = (XElement)doc.FirstNode;
                XNamespace wsse =  envelope.GetNamespaceOfPrefix("wsse");
                string username = envelope.Descendants(wsse + "Username").FirstOrDefault().Value;
    
            }
        }
    }
    ​
    

    【讨论】:

    • 谢谢,但这行不通,因为我事先不知道命名空间(甚至是 XML 的样子)。
    • 要搜索 xml,您需要知道要查找的内容。你的反应没有任何意义。
    • 我动态查询一个未知的 XML 文档,所以 XPath 和命名空间只会在运行时知道,而不是编译时知道。
    • 您可以用变量替换任何硬编码字符串(双引号),这样运行时它就可以是动态的。
    【解决方案2】:

    我有同样的要求,找到了这样的解决方案:

    XDocument doc = ...
    var nsmngr = new XmlNamespaceManager(doc.CreateReader().NameTable);
    
    foreach (var attribute in doc.Descendants().Attributes().Where(a => a.IsNamespaceDeclaration))
    {
        nsmngr.AddNamespace(attribute.Name.LocalName, attribute.Value);
    }
    
    // then use the namespacemanager when querying the document
    var someXPath = "/Envelope/Header[1]/Security[1]/UsernameToken[1]/Username[1]";
    doc.XPathEvaluate(someXPath, nsmngr)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-24
      • 1970-01-01
      • 2015-11-14
      相关资源
      最近更新 更多