The TXMLDocument of classes allow you to manipulate XML files VCL and FireMonkey applications, but this class is no direct way to call the relevant method of XPath ( selectNode , the selectNodes ), so you have to write a set of auxiliary functions to call these methods .
Usually you can write something like
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;
And used like this:
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);
The above code used in Windows MSXML provider as the default DOM vendors can work, but must run in OSX and Windows in the FireMonkey application, you must be the default DOM vendor set to ADOM (OpenXML).
DefaultDOMVendor := OpenXML4Factory.Description;
Now, if you try to use these functions (selectSingleNode, SelectNodes) in ADOM provider, you will get a very good exception
EOleException Catastrophic failure 8000FFFF
The root of this problem is Tox4DOMNode.selectNode and Tox4DOMNode.selectNodes implementation of these methods, please check the next code.
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 field Tox4DOMImplementation.InitParserAgent method is initialized, the method is never called you Tox4DOMImplementation.loadFromStream or Tox4DOMImplementation.loadxml method approach. Therefore, to solve this problem, you must call before calling selectNode and selectNodes method Tox4DOMImplementation.InitParserAgent function.
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;
By now these changes, you will be able to use ADOM supplier evaluation XPath expression VCL and FireMonkey applications.
This is a demo console application testing in Windows and 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.