如何在具有命名空间前缀的TXMLDocument上使用XPath? [英] How to use XPath on TXMLDocument which has namespace prefixes?
问题描述
<?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
:
XmlDoc.LoadFromXML(XmlString);
我是使用解决方案使用XPath查找XML节点。该解决方案在其他情况下起作用,但是当XML文档包含命名空间前缀时,它将失败。
我正在尝试访问路径:
/ soap:Envelope / soap:Body / SomeResponse / SomeResult
从链接的答案:
function selectNode(xnRoot:IXmlNode; const nodePath:WideString):IXmlNode ;
var
intfSelect:IDomNodeSelect;
dnResult:IDomNode;
intfDocAccess:IXmlDocumentAccess;
doc:TXmlDocument;
begin
结果:= nil;
如果没有分配(xnRoot)或不支持(xnRoot.DOMNode,IDomNodeSelect,intfSelect)然后
退出;
dnResult:= intfSelect.selectNode(nodePath);
如果分配(dnResult)然后
begin
如果支持(xnRoot.OwnerDocument,IXmlDocumentAccess,intfDocAccess)然后
doc:= intfDocAccess.DocumentObject
else
doc:= nil;
结果:= TXmlNode.Create(dnResult,nil,doc);
结束
结束
它在 dnResult:= intfSelect.selectNode(nodePath) code> with
EOleException
:引用未声明的命名空间前缀:'soap'
当节点名称具有命名空间前缀时,如何使其工作?
不要尝试在XPath查询中包含命名空间。
如果您想要的是SomeResult节点的文本,那么可以使用// SomeResult作为查询。由于某种原因,默认名称空间 xmlns =http:// someurl
上的缺省xml实现(msxml)barfs在 SomeResponse
parentnode。然而,使用OmniXML作为DOMVendor(= Crossplatform并且从XE7有效 - 感谢@gabr)这样做:
程序Project3;
{$ APPTYPE CONSOLE}
{$ R * .res}
使用
Xml.XmlIntf,
Xml .XMLDoc,
Xml.XMLDom,
Xml.omnixmldom,
System.SysUtils;
const
xml ='<?xml version =1.0encoding =utf-8?>'+#13#10 +
' :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>';
函数selectNode(xnRoot:IXmlNode; const nodePath:WideString):IXmlNode;
var
intfSelect:IDomNodeSelect;
dnResult:IDomNode;
intfDocAccess:IXmlDocumentAccess;
doc:TXmlDocument;
begin
结果:= nil;
如果没有分配(xnRoot)或不支持(xnRoot.DOMNode,IDomNodeSelect,intfSelect)然后
退出;
dnResult:= intfSelect.selectNode(nodePath);
如果分配(dnResult)然后
begin
如果支持(xnRoot.OwnerDocument,IXmlDocumentAccess,intfDocAccess)然后
doc:= intfDocAccess.DocumentObject
else
doc:= nil;
结果:= TXmlNode.Create(dnResult,nil,doc);
结束
结束
函数XPathQuery(Doc:IXMLDocument; Query:String):String;
var
节点:IXMLNode;
begin
结果:='';
Node:= SelectNode(Doc.DocumentElement,Query);
如果分配(Node)然后
结果:= 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')));
除了
在E:Exception do
Writeln(E.ClassName,':',E.Message);
结束
Doc:= nil;
Readln;
结束。
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>
To be cross-platform capable, this XML is loaded into Delphi's IXMLDocument
:
XmlDoc.LoadFromXML(XmlString);
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.
I'm trying to access path:
/soap:Envelope/soap:Body/SomeResponse/SomeResult
From the linked answer:
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;
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?
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屋!