如何在具有命名空间前缀的TXMLDocument上使用XPath? [英] How to use XPath on TXMLDocument which has namespace prefixes?

查看:205
本文介绍了如何在具有命名空间前缀的TXMLDocument上使用XPath?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个从第三方网络服务器收到的XML数据包:

 <?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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆