C# 将 XPath 与 XmlDocument 结合使用 - 无法选择命名空间中的节点(返回 null)



我正在尝试做一些应该很简单的事情,但我遇到了可怕的麻烦。我已经尝试了StackOverflow中多个类似问题的代码,但无济于事。 我正在尝试从澳大利亚政府的ABN查询中获取各种信息。下面是匿名返回 XML 值:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ABRSearchByABNResponse xmlns="http://abr.business.gov.au/ABRXMLSearch/">
<ABRPayloadSearchResults>
<request>
<identifierSearchRequest>
<authenticationGUID>00000000-0000-0000-0000-000000000000</authenticationGUID>
<identifierType>ABN</identifierType>
<identifierValue>00 000 000 000</identifierValue>
<history>N</history>
</identifierSearchRequest>
</request>
<response>
<usageStatement>The Registrar of the ABR monitors the quality of the information available on this website and updates the information regularly. However, neither the Registrar of the ABR nor the Commonwealth guarantee that the information available through this service (including search results) is accurate, up to date, complete or accept any liability arising from the use of or reliance upon this site.</usageStatement>
<dateRegisterLastUpdated>2017-01-01</dateRegisterLastUpdated>
<dateTimeRetrieved>2017-01-01T00:00:00.2016832+10:00</dateTimeRetrieved>
<businessEntity>
<recordLastUpdatedDate>2017-01-01</recordLastUpdatedDate>
<ABN>
<identifierValue>00000000000</identifierValue>
<isCurrentIndicator>Y</isCurrentIndicator>
<replacedFrom>0001-01-01</replacedFrom>
</ABN>
<entityStatus>
<entityStatusCode>Active</entityStatusCode>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</entityStatus>
<ASICNumber>000000000</ASICNumber>
<entityType>
<entityTypeCode>PRV</entityTypeCode>
<entityDescription>Australian Private Company</entityDescription>
</entityType>
<goodsAndServicesTax>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</goodsAndServicesTax>
<mainName>
<organisationName>COMPANY LTD</organisationName>
<effectiveFrom>2017-01-01</effectiveFrom>
</mainName>
<mainBusinessPhysicalAddress>
<stateCode>NSW</stateCode>
<postcode>0000</postcode>
<effectiveFrom>2017-01-01</effectiveFrom>
<effectiveTo>0001-01-01</effectiveTo>
</mainBusinessPhysicalAddress>
</businessEntity>
</response>
</ABRPayloadSearchResults>
</ABRSearchByABNResponse>
</soap:Body>
</soap:Envelope>

所以我想使用xpath="//response"获取整个响应,然后在该节点中使用各种 xpath 语句来获取<organisationName>("//mainName/organisationName"(和其他值。 应该很简单吧?在记事本++中测试时,这些xpath语句似乎有效,但我在Visual Studio中使用此代码:

XmlDocument xdoc = new XmlDocument();
xdoc.LoadXml(ipxml);
XmlNode xnode = xdoc.SelectSingleNode("//response");
XmlNodeList xlist = xdoc.SelectNodes("//mainName/organisationName");
xlist = xdoc.GetElementsByTagName("mainName");

但它总是返回 null,无论我在 xpath 中放入什么,无论我是否选择具有子节点、值的内容,我都会得到节点的null返回和列表的 0 计数。 我可以使用GetElementsByTagName()获取节点,如返回正确节点的示例所示,但我想使用 xpath "正确"选择正确的字段。

我也尝试使用XElement和Linq,但仍然没有运气。XML 有什么奇怪的地方吗?

我敢肯定,这一定很简单,但我已经挣扎了很长时间。

您没有处理文档中存在的命名空间。具体来说,高级元素:

<ABRSearchByABNResponse xmlns="http://abr.business.gov.au/ABRXMLSearch/">

ABRSearchByABNResponse及其所有子元素(除非被另一个xmlns覆盖(放入命名空间http://abr.business.gov.au/ABRXMLSearch/。为了导航到这些节点(没有像GetElementsByTagName这样的黑客或使用local-name()(,你需要像这样向XmlNamespaceManager注册命名空间。xmlns别名不一定需要与原始文档中使用的别名匹配,但这样做是一个很好的约定:

XmlDocument

var xdoc = new XmlDocument();
var ns = new XmlNamespaceManager(xdoc.NameTable);
ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("abr", "http://abr.business.gov.au/ABRXMLSearch/");
xdoc.LoadXml(ipxml);
// NB need to use the overload accepting a namespace
var xresponse = xdoc.SelectSingleNode("//abr:response", ns);
var xlist = xdoc.SelectNodes("//abr:mainName/abr:organisationName", ns);

XDocument

最近,LINQ 的强大功能可以通过 XDocument 利用,这使得使用命名空间变得更加容易(Descendants在任何深度找到子节点(

var xdoc = XDocument.Parse(ipxml);
XNamespace soap = "http://schemas.xmlsoap.org/soap/envelope/";
XNamespace abr = "http://abr.business.gov.au/ABRXMLSearch/";
var xresponse = xdoc.Descendants(abr + "response");
var xlist = xdoc.Descendants(abr + "organisationName");

XDocument + XPath

您也可以在 Linq to XML 中使用 XPath,特别是对于更复杂的表达式:

var xdoc = XDocument.Parse(ipxml);
var ns = new XmlNamespaceManager(new NameTable());
ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
ns.AddNamespace("abr", "http://abr.business.gov.au/ABRXMLSearch/");
var xresponse = xdoc.XPathSelectElement("//abr:response", ns);
var xlist = xdoc.XPathSelectElement("//abr:mainName/abr:organisationName", ns);

您需要在 DocumentElement 上调用SelectSingleNodeSelectNodes。您是在文档本身上调用它们。

例如:

XmlNode xnode = xdoc.DocumentElement.SelectSingleNode("//response");

最新更新