如何在具有命名空间前缀的 TXMLDocument 上使用 XPath? [英] How to use XPath on TXMLDocument which has namespace prefixes?
问题描述
我从第三方 Web 服务器收到了一个 XML 数据包:
I have an XML packet received from a third-party web server:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<SomeResponse xmlns="http://someurl">
<SomeResult>
.....
</SomeResult>
</SomeResponse>
</soap:Body>
</soap:Envelope>
为了能够跨平台,这个 XML 被加载到 Delphi 的 IXMLDocument
中:
To be cross-platform capable, this XML is loaded into Delphi's IXMLDocument
:
XmlDoc.LoadFromXML(XmlString);
我正在使用解决方案使用 XPath 查找 XML 节点.该解决方案适用于其他情况,但是当 XML 文档包含名称空间前缀时,它会失败.
I'm using a solution to find an XML node using XPath. The solution works in other cases, however when the XML document contains namespace prefixes, it fails.
我正在尝试访问路径:
/soap:Envelope/soap:Body/SomeResponse/SomeResult
来自链接的答案:
function selectNode(xnRoot: IXmlNode; const nodePath: WideString): IXmlNode;
var
intfSelect : IDomNodeSelect;
dnResult : IDomNode;
intfDocAccess : IXmlDocumentAccess;
doc: TXmlDocument;
begin
Result := nil;
if not Assigned(xnRoot) or not Supports(xnRoot.DOMNode, IDomNodeSelect, intfSelect) then
Exit;
dnResult := intfSelect.selectNode(nodePath);
if Assigned(dnResult) then
begin
if Supports(xnRoot.OwnerDocument, IXmlDocumentAccess, intfDocAccess) then
doc := intfDocAccess.DocumentObject
else
doc := nil;
Result := TXmlNode.Create(dnResult, nil, doc);
end;
end;
它在 dnResult := intfSelect.selectNode(nodePath);
处失败,带有 EOleException
:引用未声明的命名空间前缀:'soap'
It fails at dnResult := intfSelect.selectNode(nodePath);
with EOleException
: Reference to undeclared namespace prefix: 'soap'
当节点名称具有命名空间前缀时,我如何使其工作?
How do I make this work when the node names have a namespace prefix?
推荐答案
不要尝试在 XPath 查询中包含名称空间.如果你想要的只是 SomeResult 节点的文本,那么你可以使用 '//SomeResult' 作为查询.出于某种原因,默认的 xml 实现 (msxml) 在 SomeResponse
父节点上的默认命名空间 xmlns="http://someurl"
上阻塞.但是,使用 OmniXML 作为 DOMVendor(= Crossplatform 并且从 XE7 开始有效 - 感谢 @gabr)这是有效的:
Do not try to include namespaces in your XPath query.
If all you want is the text of the SomeResult node, then you can use '//SomeResult' as query. For some reason the default xml implementation (msxml) barfs on the default namespace xmlns="http://someurl"
on the SomeResponse
parentnode. However, using OmniXML as the DOMVendor (= Crossplatform and valid from XE7 - thanks to @gabr) this works:
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Xml.XmlIntf,
Xml.XMLDoc,
Xml.XMLDom,
Xml.omnixmldom,
System.SysUtils;
const
xml = '<?xml version="1.0" encoding="utf-8"?>'+#13#10+
'<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'+#13#10+
'xmlns:xsd="http://www.w3.org/2001/XMLSchema"'+#13#10+
'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">'+#13#10+
' <soap:Body>'+#13#10+
' <SomeResponse xmlns="http://tempuri.org">'+#13#10+
' <SomeResult>1</SomeResult>'+#13#10+
' </SomeResponse>'+#13#10+
' </soap:Body>'+#13#10+
'</soap:Envelope>';
function selectNode(xnRoot: IXmlNode; const nodePath: WideString): IXmlNode;
var
intfSelect : IDomNodeSelect;
dnResult : IDomNode;
intfDocAccess : IXmlDocumentAccess;
doc: TXmlDocument;
begin
Result := nil;
if not Assigned(xnRoot) or not Supports(xnRoot.DOMNode, IDomNodeSelect, intfSelect) then
Exit;
dnResult := intfSelect.selectNode(nodePath);
if Assigned(dnResult) then
begin
if Supports(xnRoot.OwnerDocument, IXmlDocumentAccess, intfDocAccess) then
doc := intfDocAccess.DocumentObject
else
doc := nil;
Result := TXmlNode.Create(dnResult, nil, doc);
end;
end;
function XPathQuery(Doc : IXMLDocument; Query : String) : String;
var
Node : IXMLNode;
begin
Result := '';
Node := SelectNode(Doc.DocumentElement, Query);
if Assigned(Node) then
Result := Node.Text
end;
var
Doc : IXMLDocument;
begin
DefaultDOMVendor := sOmniXmlVendor;
Doc := TXMLDocument.Create(nil);
try
Doc.LoadFromXML(Xml);
Writeln(Doc.XML.Text);
Writeln(XPathQuery(Doc, '//SomeResult'));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Doc := nil;
Readln;
end.
这篇关于如何在具有命名空间前缀的 TXMLDocument 上使用 XPath?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!