该TXMLDocument的类让你来操作VCL和FireMonkey应用程序的XML文件,但这个类没有实现直接的方式来调用XPath的相关方法(selectNode,的selectNodes),所以你必须编写一组辅助函数来调用这些方法。
通常你可以写这样的东西
function selectSingleNode(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNode; var LDomNodeSelect : IDomNodeSelect; begin if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then Exit; //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect); Result:=LDomNodeSelect.selectNode(nodePath); end;
function SelectNodes(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNodeList; var LDomNodeSelect : IDomNodeSelect; begin if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then Exit; //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect); Result:=LDomNodeSelect.selectNodes(nodePath); end;
并这样使用:
var XmlDoc: IXMLDocument; LNode : IDOMNode; i : Integer; begin XmlDoc := TXMLDocument.Create(nil); XmlDoc.Active := True; XmlDoc.Options := XmlDoc.Options + [doNodeAutoIndent]; XmlDoc.Version := '1.0'; ... ... LNode:=selectSingleNode(XmlDoc.DOMDocument,XPathExpr);
上面的代码在Windows下使用MSXML提供程序作为默认DOM供应商可以正常工作,但在必须在OSX和Windows中运行的FireMonkey应用程序中,必须将默认DOM供应商设置为ADOM(OpenXML)。
DefaultDOMVendor := OpenXML4Factory.Description;
现在,如果您尝试在ADOM供应商下使用上述功能(selectSingleNode,SelectNodes),您将获得一个非常好的例外
EOleException Catastrophic failure 8000FFFF
这个问题的根目录是Tox4DOMNode.selectNode和Tox4DOMNode.selectNodes这些方法的实现,请检查下一个代码。
function Tox4DOMNode.selectNode(const nodePath: WideString): IDOMNode; var xpath: TXpathExpression; xdomText: TDomText; begin Result := nil; if not Assigned(WrapperDocument) or not Assigned(WrapperDocument.WrapperDOMImpl) then Exit; xpath := WrapperDocument.WrapperDOMImpl.FXpath; //here the xpath is set with a nil value because the FXpath was no initialized xpath.ContextNode := NativeNode; //Here the App crash because xpath is nil
FXpath字段在Tox4DOMImplementation.InitParserAgent方法中初始化,该方法永远不会调用您使用Tox4DOMImplementation.loadFromStream或Tox4DOMImplementation.loadxml方法的方法。因此,要解决此问题,必须在调用selectNode和selectNodes方法之前调用Tox4DOMImplementation.InitParserAgent函数。
function selectSingleNode(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNode; var LDomNodeSelect : IDomNodeSelect; begin if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then Exit; //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect); if (DefaultDOMVendor = OpenXML4Factory.Description) then Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent; Result:=LDomNodeSelect.selectNode(nodePath); end;
function SelectNodes(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNodeList; var LDomNodeSelect : IDomNodeSelect; begin if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then Exit; //or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect); if (DefaultDOMVendor = OpenXML4Factory.Description) then Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent; Result:=LDomNodeSelect.selectNodes(nodePath); end;
现在通过这些更改,您将能够使用ADOM供应商评估VCL和FireMonkey应用程序中的XPath表达式。
这是在Windows和OSX (Delphi 10.3.2 Rio)中测试的演示控制台应用程序:
1 // reference https://theroadtodelphi.wordpress.com/2013/05/29/enabling-xpath-selectnode-selectnodes-methods-in-vcl-and-firemonkey-apps/ 2 3 {$APPTYPE CONSOLE} 4 uses 5 {$IFDEF MSWINDOWS} 6 System.Win.ComObj, 7 Winapi.ActiveX, 8 {$ENDIF} 9 System.SysUtils, 10 Xml.XMLIntf, 11 Xml.adomxmldom, 12 Xml.XMLDom, 13 Xml.XMLDoc; 14 15 function selectSingleNode(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNode; 16 var 17 LDomNodeSelect: IDomNodeSelect; 18 begin 19 if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then 20 Exit; 21 // or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect); 22 if (DefaultDOMVendor = OpenXML4Factory.Description) then 23 Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent; 24 Result := LDomNodeSelect.selectNode(nodePath); 25 end; 26 27 function SelectNodes(ADOMDocument: IDOMDocument; const nodePath: WideString): IDOMNodeList; 28 var 29 LDomNodeSelect: IDomNodeSelect; 30 begin 31 if not Assigned(ADOMDocument) or not Supports(ADOMDocument.documentElement, IDomNodeSelect, LDomNodeSelect) then 32 Exit; 33 // or just LDomNodeSelect:= (ADOMDocument.documentElement as IDOMNodeSelect); 34 if (DefaultDOMVendor = OpenXML4Factory.Description) then 35 Tox4DOMNode(LDomNodeSelect).WrapperDocument.WrapperDOMImpl.InitParserAgent; 36 Result := LDomNodeSelect.SelectNodes(nodePath); 37 end; 38 39 procedure TestXPath; 40 var 41 XMLDoc: IXMLDocument; 42 Root, Book, Author, Publisher: IXMLNode; 43 LNodeList: IDOMNodeList; 44 LNode: IDOMNode; 45 i: Integer; 46 begin 47 XMLDoc := TXMLDocument.Create(nil); 48 XMLDoc.Active := True; 49 XMLDoc.Options := XMLDoc.Options + [doNodeAutoIndent]; 50 XMLDoc.Version := '1.0'; 51 52 Root := XMLDoc.CreateNode('BookStore'); 53 Root.Attributes['url'] := 'http://www.amazon.com'; 54 XMLDoc.documentElement := Root; 55 56 Book := XMLDoc.CreateNode('Book'); 57 Book.Attributes['Name'] := 'Steve Jobs'; 58 Author := XMLDoc.CreateNode('Author'); 59 Author.Text := 'Walter Isaacson'; 60 Publisher := XMLDoc.CreateNode('Publisher'); 61 Publisher.Text := 'Simon Schuster (October 24, 2011)'; 62 Root.ChildNodes.Add(Book); 63 Book.ChildNodes.Add(Author); 64 Book.ChildNodes.Add(Publisher); 65 66 Book := XMLDoc.CreateNode('Book'); 67 Book.Attributes['Name'] := 'Clean Code: A Handbook of Agile Software Craftsmanship'; 68 Author := XMLDoc.CreateNode('Author'); 69 Author.Text := 'Robert C. Martin'; 70 Publisher := XMLDoc.CreateNode('Publisher'); 71 Publisher.Text := 'Prentice Hall; 1 edition (August 11, 2008)'; 72 Root.ChildNodes.Add(Book); 73 Book.ChildNodes.Add(Author); 74 Book.ChildNodes.Add(Publisher); 75 76 Book := XMLDoc.CreateNode('Book'); 77 Book.Attributes['Name'] := 'Paradox Lost'; 78 Author := XMLDoc.CreateNode('Author'); 79 Author.Text := 'Kress, Peter'; 80 Publisher := XMLDoc.CreateNode('Publisher'); 81 Publisher.Text := 'Prentice Hall; 1 edition (February 2, 2000)'; 82 Root.ChildNodes.Add(Book); 83 Book.ChildNodes.Add(Author); 84 Book.ChildNodes.Add(Publisher); 85 86 Writeln(XMLDoc.Xml.Text); 87 88 Writeln('selectSingleNode'); 89 LNode := selectSingleNode(XMLDoc.DOMDocument, '/BookStore/Book[2]/Author["Robert C. Martin"]'); 90 if LNode <> nil then 91 Writeln(LNode.firstChild.nodeValue); 92 93 Writeln; 94 95 Writeln('SelectNodes'); 96 LNodeList := SelectNodes(XMLDoc.DOMDocument, '//BookStore/Book/Author'); 97 if LNodeList <> nil then 98 for i := 0 to LNodeList.length - 1 do 99 Writeln(LNodeList[i].firstChild.nodeValue); 100 end; 101 102 begin 103 try 104 ReportMemoryLeaksOnShutdown := True; 105 DefaultDOMVendor := OpenXML4Factory.Description; 106 {$IFDEF MSWINDOWS}CoInitialize(nil); {$ENDIF} 107 try 108 TestXPath; 109 finally 110 {$IFDEF MSWINDOWS}CoUninitialize; {$ENDIF} 111 end; 112 except 113 {$IFDEF MSWINDOWS} 114 on E: EOleException do 115 Writeln(Format('EOleException %s %x', [E.Message, E.ErrorCode])); 116 {$ENDIF} 117 on E: Exception do 118 Writeln(E.Classname, ':', E.Message); 119 end; 120 Writeln; 121 Writeln('Press Enter to exit'); 122 Readln; 123 124 end.