当XML具有名称空间时,如何使用XPath动态查询XDocument

时间:2015-12-26 01:32:12

标签: c# xml xpath

我一直在使用this absolute XPath extensionMail::queue('Project.Emails.Award', $data, function($message) { $message ->to('EmailAddress', 'EmailAddress') ->subject('hi'); }); 内获取XPath个节点字符串。然后,XPath可以使用XDocument查询节点。

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

xdoc.XPathSelectElement(xpath)

<?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> 节点上调用GetAbsoluteXPath会给出:

Username

但是,使用XPathSelectElement和上面的XPath查询XDocument会产生/Envelope/Header[1]/Security[1]/UsernameToken[1]/Username[1] ,因为没有指定名称空间。

根据我查找的答案,解决方案是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和命名空间是在运行时确定的,而不是在编译时确定的。因此,手动添加仅适用于此特定示例的字符串通常不起作用。

2 个答案:

答案 0 :(得分:0)

下面的代码是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;

        }
    }
}
​

答案 1 :(得分:0)

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

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)