c# - 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") 和其他值。应该很简单吧?在 Notepad++ 中进行测试时,这些 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/">
placeABRSearchByABNResponse
及其所有子元素(除非被另一个元素覆盖xmlns
)进入命名空间http://abr.business.gov.au/ABRXMLSearch/
。为了导航到这些节点(不使用 hackGetElementsByTagName
或 using local-name()
),您需要使用 注册命名空间XmlNamespaceManager
,就像这样。xmlns
别名不一定需要与原始文档中使用的别名相匹配,但这样做是一个很好的约定:
xml文档
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);
推荐阅读
- java - 使用 Google 登录检索用户信息
- ios - 在 CloudKit 中共享父记录时,子记录未显示在共享数据库中
- java - Flutter build release APK 报 NoSuchMethodException 错误
- c# - 如何使用刚体的 AddForce 设置恒定速度?
- linux - 如何计算 /etc 目录中的配置文件数
- powerbi - 客户 DAX Power BI 的平均订单大小
- flutter - flutter,我要求删除的索引值与List中实际删除的索引值不同
- reactjs - 如何从反应中的下拉列表中删除重复值
- python - 匹配两个 csv 文件中的字符串,但第二个文件太大而无法读入列表
- c# - dotnet publish -c 发布到自包含的 exe 不起作用