The TXMLDocument class allow you to manipulate XML files in VCL and FireMonkey Apps, but this class doesn’t implements a direct way to call the XPath related methods (selectNode, selectNodes) , so you must write a set of helper functions to call these methods.
Normally you can write something like so
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 use like so.
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 will works fine under Windows using the MSXML provider as Default DOM Vendor, but in a FireMonkey Application which must run in OSX and Windows you must set the Default DOM Vendor to ADOM (OpenXML).
DefaultDOMVendor := OpenXML4Factory.Description;
Now if you try to use the above functions (selectSingleNode, SelectNodes) under the ADOM vendor you will get an awfull exception
EOleException Catastrophic failure 8000FFFF
The root of this issue is located in the Tox4DOMNode.selectNode and Tox4DOMNode.selectNodes implementation of these methods, 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
The FXpath field is initialized in the Tox4DOMImplementation.InitParserAgent method which is never call at least which you uses the Tox4DOMImplementation.loadFromStream or Tox4DOMImplementation.loadxml methods. So to fix this issue you must call the Tox4DOMImplementation.InitParserAgent function before to call the selectNode and selectNodes methods.
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;
Now with these changes you will able to evaluate XPath expressions in VCL and FireMonkey Apps using the ADOM vendor.
This is a demo console App tested in Windows and OSX (XE2 and XE4)
{$APPTYPE CONSOLE} uses {$IFDEF MSWINDOWS} System.Win.ComObj, Winapi.ActiveX, {$ENDIF} System.SysUtils, Xml.XMLIntf, Xml.adomxmldom, Xml.XMLDom, Xml.XMLDoc; 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; procedure TestXPath; var XmlDoc: IXMLDocument; Root, Book, Author, Publisher : IXMLNode; LNodeList : IDOMNodeList; LNode : IDOMNode; i : Integer; begin XmlDoc := TXMLDocument.Create(nil); XmlDoc.Active := True; XmlDoc.Options := XmlDoc.Options + [doNodeAutoIndent]; XmlDoc.Version := '1.0'; Root := XmlDoc.CreateNode('BookStore'); Root.Attributes['url'] := 'http://www.amazon.com'; XmlDoc.DocumentElement := Root; Book := XmlDoc.CreateNode('Book'); Book.Attributes['Name'] := 'Steve Jobs'; Author := XmlDoc.CreateNode('Author'); Author.Text := 'Walter Isaacson'; Publisher := XmlDoc.CreateNode('Publisher'); Publisher.Text := 'Simon Schuster (October 24, 2011)'; Root.ChildNodes.Add(Book); Book.ChildNodes.Add(Author); Book.ChildNodes.Add(Publisher); Book := XmlDoc.CreateNode('Book'); Book.Attributes['Name'] := 'Clean Code: A Handbook of Agile Software Craftsmanship'; Author := XmlDoc.CreateNode('Author'); Author.Text := 'Robert C. Martin'; Publisher := XmlDoc.CreateNode('Publisher'); Publisher.Text := 'Prentice Hall; 1 edition (August 11, 2008)'; Root.ChildNodes.Add(Book); Book.ChildNodes.Add(Author); Book.ChildNodes.Add(Publisher); Book := XmlDoc.CreateNode('Book'); Book.Attributes['Name'] := 'Paradox Lost'; Author := XmlDoc.CreateNode('Author'); Author.Text := 'Kress, Peter'; Publisher := XmlDoc.CreateNode('Publisher'); Publisher.Text := 'Prentice Hall; 1 edition (February 2, 2000)'; Root.ChildNodes.Add(Book); Book.ChildNodes.Add(Author); Book.ChildNodes.Add(Publisher); Writeln(XmlDoc.XML.Text); Writeln('selectSingleNode'); LNode:=selectSingleNode(XmlDoc.DOMDocument,'/BookStore/Book[2]/Author["Robert C. Martin"]'); if LNode<>nil then Writeln(LNode.firstChild.nodeValue); Writeln; Writeln('SelectNodes'); LNodeList:=SelectNodes(XmlDoc.DOMDocument,'//BookStore/Book/Author'); if LNodeList<>nil then for i := 0 to LNodeList.length-1 do Writeln(LNodeList[i].firstChild.nodeValue); end; begin try ReportMemoryLeaksOnShutdown:=True; DefaultDOMVendor := OpenXML4Factory.Description; {$IFDEF MSWINDOWS}CoInitialize(nil);{$ENDIF} try TestXPath; finally {$IFDEF MSWINDOWS}CoUninitialize;{$ENDIF} end; except {$IFDEF MSWINDOWS} on E:EOleException do Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode])); {$ENDIF} on E:Exception do Writeln(E.Classname, ':', E.Message); end; Writeln; Writeln('Press Enter to exit'); Readln; end.
![](http://stats.wordpress.com/b.gif?host=theroadtodelphi.wordpress.com&blog=9574973&post=3432&subd=theroadtodelphi&ref=&feed=1)