![]() |
XPathQuery über IXMLNode
Da ich keine Stelle finden konnte an der eine komplette Abhandlung zum Thema XPath mit Delphi auffindbar war: Veröffentliche ich die hart erarbeitete Lösung für Alle.
Benötigte Uses: msxml, xmldom, XMLDoc, XmlIntf
Delphi-Quellcode:
// Originale Quelle: Zarko Gajic:
// http://delphi.about.com/od/vclusing/qt/delphi-select-xml-nodes-ixmlnodelist-selectnodes-xpath-xmldom.htm, 18.06.2013 // Erweitert um Rekursive Parents. function XPathQuery(aNode: IXMLNode; aQuery: string): IXMLNodeList; overload; var XmlNodeAccess: IXmlNodeAccess; XmlDocAccess: IXmlDocumentAccess; XmlDomNodeSelect: IDomNodeSelect; DomNodeList: IDomNodeList; Document: TXMLDocument; i: integer; OwnerDoc: TXMLDocument; DomDoc2: IXMLDOMDocument2; function CreateWithParentNode(const aDomNode: IDOMNode; const aOwnerDoc: TXMLDocument): TXmlNode; begin if assigned(aDomNode) then Result := TXMLNode.Create(aDomNode, CreateWithParentNode(aDomNode.parentNode, aOwnerDoc), aOwnerDoc) else Result := nil; end; begin Result := nil; if not assigned(aNode) then Exit; if not Supports(aNode, IXmlNodeAccess, XmlNodeAccess) then raise Exception.Create('Interface IXmlNodeAccess not found.'); if not Supports(aNode.DOMNode, IDomNodeSelect, XmlDomNodeSelect) then raise Exception.Create('Interface IDomNodeSelect not found.'); if Supports(aNode.OwnerDocument, IXmlDocumentAccess, XmlDocAccess) then OwnerDoc := XmlDocAccess.DocumentObject else OwnerDoc := nil; // if Owner is nil this is a possble Memory Leak! //>>> if XPath is not enabled if assigned(OwnerDoc) then if Supports(OwnerDoc.DOMDocument, IXMLDOMDocument2, DomDoc2) then DomDoc2.setProperty('SelectionLanguage', 'XPath'); //<<< if XPath is not enabled DomNodeList := XmlDomNodeSelect.selectNodes(aQuery); if assigned(DomNodeList) then begin Result := TXMLNodeList.Create(XmlNodeAccess.GetNodeObject, '', nil); Document := OwnerDoc; for i := 0 to pred(DomNodeList.length) do begin Result.Add(CreateWithParentNode(DomNodeList.item[i], Document)); end; end end; |
AW: XPathQuery über IXMLNode
Interessante Lösung. Ich war bisher immer über xsl und transformNode gegangen. Sprich zum Beispiel:
Delphi-Quellcode:
private
FXmlDoc: DOMDocument60; ... var StyleSheetDoc: DOMDocument60; begin StyleSheetDoc := CoDOMDocument60.Create; StyleSheetDoc.async := False; if StyleSheetDoc.loadXML('<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">'#13#10 + '<xsl:output method="text" />'#13#10 + '<xsl:template match="/">'#13#10 + '<xsl:value-of select="' + BuildXPathQuery(...) + '" />'#13#10 + '</xsl:template>'#13#10 + '</xsl:stylesheet>') then Result := FXmlDoc.transformNode(StyleSheetDoc) else Result := ''; end; |
AW: XPathQuery über IXMLNode
Im Prinzip wäre ein Link zu einem netten XPath-Tutorial nicht schlecht, oder notalls zur Wiki, aber zumindestens ein kleines Beispiel.
Nicht jeder kennt ja die Syntax oder weiß was XPath überhaupt ist. Täuscht das, oder baust du die XML nach, nur halt mit den rausgefilterten Knoten und ihren Elternknoten? Bzw. es wird alles entfernt, was nicht via XPath ausgewählt wurde oder ein Elternknoten eines ausgewählten knotens ist. Wenn möglich solltest du niemals Interface-Instanzen und Object-Instanzen eines Objektes miteinander vermischen. CreateWithParentNode sollte momit also IXMLNode als Result besitzen. [edit] hab grade gemerkt, daß TXMLNode.Create das als Objekt haben will. (nur für TXMLNodeList.Add hätte ds aber gestimmt) Und die Variable Document war doch eher nutzlos, da sie den Wert von OwnerDoc enthilt, womit man also gleich OwnerDoc verwenden kann. Da, wo ich mal mit XPath rumgespielt hab, bin ich zwar direkt über IXMLDOMDocument und Co. gegangen, aber das sollte doch egal sein. > siehe TLanguage im ![]() Zitat:
Nja, zumindestens hat sich seit Jahren noch keiner beschwert, daß es nicht funktionierte, auch wenn ich es nicht erst aktiviert hatte. An solchen Stellen, wo ein interface theoretisch eh immer unterstützt wird, da hab ich mir dieses Supports irgendwie abgewöhnt, vorallem wenn diese interfaces sowieso benötigt werden, damit die Funktion gewährleistet ist. Aber ich weiß jetzt nicht mehr genau, seit welcher Delphiversion das geht.
Delphi-Quellcode:
XmlDocAccess := OwnerDoc.DOMDocument as IXMLDOMDocument2;
Notfalls wirft AS auch eine passende Exception.
Delphi-Quellcode:
function XPathQuery(aNode: IXMLNode; aQuery: string): IXMLNodeList; overload;
var XmlDocAccess: IXmlDocumentAccess; DomNodeList: IDomNodeList; i: integer; OwnerDoc: TXMLDocument; DomDoc2: IXMLDOMDocument2; function CreateWithParentNode(const aDomNode: IDOMNode; const aOwnerDoc: TXMLDocument): IXMLNode; begin if Assigned(aDomNode) then Result := TXMLNode.Create(aDomNode, CreateWithParentNode(aDomNode.parentNode, aOwnerDoc), aOwnerDoc) else Result := nil; end; begin Result := nil; if not assigned(aNode) then Exit; if Supports(aNode.OwnerDocument, IXmlDocumentAccess, XmlDocAccess) then OwnerDoc := XmlDocAccess.DocumentObject else OwnerDoc := nil; // if Owner is nil this is a possble Memory Leak! // if XPath is not enabled if Assigned(OwnerDoc) and Supports(OwnerDoc.DOMDocument, IXMLDOMDocument2, DomDoc2) then DomDoc2.setProperty('SelectionLanguage', 'XPath'); DomNodeList := (aNode.DOMNode as IDomNodeSelect).selectNodes(aQuery); if Assigned(DomNodeList) then begin Result := TXMLNodeList.Create((aNode as IXmlNodeAccess).GetNodeObject, '', nil); for i := 0 to Pred(DomNodeList.length) do Result.Add(CreateWithParentNode(DomNodeList.item[i], OwnerDoc)); end; end; |
AW: XPathQuery über IXMLNode
In XSL bin ich nicht fit genug, um überhaupt auf die Idee zu kommen. Nett.
Ich habe bislang praktisch 1:1 die Lösung vom guten Zarko Gajic übernommen.
Delphi-Quellcode:
Mehr habe ich bislang nie gebraucht. Im Endeffekt das selbe über
unit XpathHelper;
interface uses Xml.XMLIntf; type // Quelle: Zarko Gajic: // http://delphi.about.com/od/vclusing/qt/delphi-select-xml-nodes-ixmlnodelist-selectnodes-xpath-xmldom.htm, 18.06.2013 // und // http://delphi.about.com/od/delphi-tips-2011/qt/select-single-node-ixmlnode-txmlnode-xpath-delphi-xmldom.htm, 18.06.2013 /// <summary> /// Hilfsklasse, gibt <c>IXMLNode</c> bzw. <c>IXMLNodeList</c> für einen /// entsprechenden XPath zurück /// </summary> TXpathHelper = class class function SelectNode(xnRoot: IXmlNode; const nodePath: WideString): IXmlNode; class function SelectNodes(xnRoot: IXmlNode; const nodePath: WideString): IXMLNodeList; end; implementation uses System.SysUtils, Xml.XMLDOM, Xml.XMLDoc ; class function TXPathHelper.SelectNodes(xnRoot: IXmlNode; const nodePath: WideString): IXMLNodeList; var intfSelect: IDomNodeSelect; intfAccess: IXmlNodeAccess; dnlResult: IDomNodeList; intfDocAccess: IXmlDocumentAccess; doc: TXmlDocument; i: Integer; dn: IDomNode; begin Result := nil; if not Assigned(xnRoot) or not Supports(xnRoot, IXmlNodeAccess, intfAccess) or not Supports(xnRoot.DOMNode, IDomNodeSelect, intfSelect) then Exit; dnlResult := intfSelect.SelectNodes(nodePath); if Assigned(dnlResult) then begin Result := TXmlNodeList.Create(intfAccess.GetNodeObject, '', nil); if Supports(xnRoot.OwnerDocument, IXmlDocumentAccess, intfDocAccess) then doc := intfDocAccess.DocumentObject else doc := nil; for i := 0 to dnlResult.length - 1 do begin dn := dnlResult.item[i]; Result.Add(TXmlNode.Create(dn, nil, doc)); end; end; end; class function TXPathHelper.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; end.
Delphi-Quellcode:
IDomNodeSelect::selectNode(Str)
|
AW: XPathQuery über IXMLNode
Zitat:
Das geht mit selectNodes nicht nehme ich an. |
AW: XPathQuery über IXMLNode
Danke für die Verbesserungen. (liest sich vor allem nicht mehr so klobig, wenn jemand von Euch den Post besser einsortieren kann, würde ich mich freuen wenn das passiert, ich selbst konnte keine bessere Stelle finden.)
Es ist nur notwendig den Aufbau so zu gestalten wenn man nach der XPath Abfrage weiterhin mit den Xml Objekten von Delphi arbeiten will. Ziel ist es eine voll funktionsfähige IXMLNodeList zu erhalten und ohne DOM spezifische Interfaces aus zu kommen. Die zuvor erwähnte Methode von "Zarko Gajic" kann mir bei meiner Recherche ebenfalls über den weg gelaufen sein. (nur weiß ich nicht mehr wie und wo ich alles zusammengesucht habe). Das was ich gepostet habe erstellt nur zusätzlich rekursiv die Parents. |
AW: XPathQuery über IXMLNode
Zitat:
Nja, zumindestens kann man sich eine entsprechende NodeList geben lassen und davon die .length nehmen. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:29 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz