AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

XPathQuery über IXMLNode

Ein Thema von KridSElot · begonnen am 19. Nov 2013 · letzter Beitrag vom 19. Nov 2013
Antwort Antwort
KridSElot

Registriert seit: 7. Sep 2006
Ort: Bad Kreuznach
3 Beiträge
 
#1

XPathQuery über IXMLNode

  Alt 19. Nov 2013, 09:33
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;

Geändert von KridSElot (19. Nov 2013 um 11:35 Uhr) Grund: Quelle durch Hinweis wieder gefunden. Ich habe Delphi.About.com wegen der ganzen Werbung für nen News Crawler gehalten.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.582 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: XPathQuery über IXMLNode

  Alt 19. Nov 2013, 09:41
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;
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.062 Beiträge
 
Delphi 12 Athens
 
#3

AW: XPathQuery über IXMLNode

  Alt 19. Nov 2013, 10:35
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 Hier im Forum suchenManifest-Creator

Zitat:
DomDoc2.setProperty('SelectionLanguage', 'XPath');
Ich wußte garnicht, daß man es vorher notfalls erst aktivieren muß?
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.
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;
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.

Geändert von himitsu (19. Nov 2013 um 10:39 Uhr)
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.158 Beiträge
 
Delphi 10 Seattle Enterprise
 
#4

AW: XPathQuery über IXMLNode

  Alt 19. Nov 2013, 11:15
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:
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.
Mehr habe ich bislang nie gebraucht. Im Endeffekt das selbe über IDomNodeSelect::selectNode(Str)
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.582 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: XPathQuery über IXMLNode

  Alt 19. Nov 2013, 11:21
In XSL bin ich nicht fit genug, um überhaupt auf die Idee zu kommen.
Damit kann man noch mehr machen. Damit kann ich direkt aus einer XML-Datenstruktur z.B. den n-ten Vornamen aus einer Liste von Personenstrukturen ermitteln. Oder die Anzahl der hinterlegten Adressen in einer Personenstruktur.
Das geht mit selectNodes nicht nehme ich an.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!
  Mit Zitat antworten Zitat
KridSElot

Registriert seit: 7. Sep 2006
Ort: Bad Kreuznach
3 Beiträge
 
#6

AW: XPathQuery über IXMLNode

  Alt 19. Nov 2013, 11:24
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.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.062 Beiträge
 
Delphi 12 Athens
 
#7

AW: XPathQuery über IXMLNode

  Alt 19. Nov 2013, 12:04
z.B. den n-ten Vornamen aus einer Liste von Personenstrukturen ermitteln. Oder die Anzahl der hinterlegten Adressen in einer Personenstruktur.
Das geht mit selectNodes nicht nehme ich an.
Gehn tut es da auch.
Nja, zumindestens kann man sich eine entsprechende NodeList geben lassen und davon die .length nehmen.
Neuste Erkenntnis:
Seit Pos einen dritten Parameter hat,
wird PoSex im Delphi viel seltener praktiziert.
  Mit Zitat antworten Zitat
Antwort Antwort

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:28 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz