XML und die Verwendung von XPath

Rund um die LCL und andere Komponenten
aXied
Beiträge: 20
Registriert: Mo 30. Sep 2013, 20:57

Re: XML und die Verwendung von XPath

Beitrag von aXied »

Ich werd versuchen das Problem zu lösen, ggf. eine Funktion dafür schreiben und mich dann zu diesem Thema wieder melden.
Vielen Dank noch mal für Deine Hilfe.

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: XML und die Verwendung von XPath

Beitrag von Socke »

aXied hat geschrieben:Ich werd versuchen das Problem zu lösen, ggf. eine Funktion dafür schreiben und mich dann zu diesem Thema wieder melden.
Du kannst auch eine vorhandene optimieren:

Code: Alles auswählen

program Project1;
 
{$mode objfpc}{$H+}
 
uses
  Classes, dom, XMLRead, XPath, sysutils
  { you can add units after this };
 
 
const
  TESTDOC = '<?xml version="1.0" encoding="UTF-8"?>'
    +'<root><c><abc id="1" /><abc id="2" /></c><b><abc id="3" /><abc id="4" /></b></root>';
 
  // Zentrale Funkion, ermittelt den XML-Pfad
  function GetNodePath(aNode: TDOMNode): DOMString;
 
    // Position des Knotens in Bezug auf gleichnamige Geschwisterknoten
    function GetPosition(aNode: TDOMNode): Integer;
    var
      nn: DOMString;
    begin
      nn := anode.NodeName;
      Result := 0;
      while assigned(aNode.PreviousSibling) do
      begin
        if aNode.NodeName = nn then
          inc(Result);
        aNode := aNode.PreviousSibling;
      end;
    end;
 
    // Anzahl gleichnamiger Geschwisterknoten
    function GetSameNameCount(aNode: TDOMNode): Integer;
    var
      nn: DOMString;
      e: TDOMElement;
    begin
      Result := 0;
      if aNode.NodeType = ELEMENT_NODE then
      begin
        nn := aNode.NodeName;
        e := TDOMElement(aNode.ParentNode.FirstChild);
        while assigned(e) do
        begin
          if e.NodeName = nn then
            inc(result);
          e := TDOMElement(e.NextSibling);
        end;
        Dec(result);
      end;
    end;
  begin
 
    if aNode.NodeType = ATTRIBUTE_NODE then
    begin
      Result := GetNodePath(TDOMAttr(aNode).OwnerElement) + '/';
      Result := Result + '@' + aNode.NodeName;
    end
    else if aNode.NodeType = ELEMENT_NODE then
    begin
      if (aNode.ParentNode <> nil) then
      begin
        Result := GetNodePath(aNode.ParentNode) + '/';
      end;
      if (aNode.NodeType = DOCUMENT_NODE) OR (GetSameNameCount(aNode) = 0)  then
        Result := Result + aNode.NodeName
      else
        Result := Result + aNode.NodeName + Format('[%d]', [GetPosition(aNode)]);
    end;
  end;
 
var
  doc: TXMLDocument;
  searchednodes: TXPathVariable;
  ss: TStringStream;
begin
  ss := TStringStream.Create(TESTDOC);
  ReadXMLFile(doc, ss);
  ss.Destroy;
  searchednodes := EvaluateXPathExpression('//*[@id="2"]/@id',doc);
  writeln('Result Type Name: ', searchednodes.TypeName);
  writeln('Path: ', GetNodePath(TDOMNode(searchednodes.AsNodeSet.Items[0])));
  searchednodes.Release;
  doc.Destroy;
  readln;
end.
Ein rekursiver Ansatz. Hat bestimmt noch einiges an Optimierungspotential. Lizenz ist LGPL mit Linking Exception (wie bei der FCL).
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

aXied
Beiträge: 20
Registriert: Mo 30. Sep 2013, 20:57

Re: XML und die Verwendung von XPath

Beitrag von aXied »

Hallo socke.
Ich habe Deinen Code ausprobiert. Erste Tests mit einfachen XPath-Abfragen wurden erfolgreich aufgelöst. :wink:

Vielen Dank für Deine Hilfe.

Sollte ich tatsächlich noch optimieren können :lol: , stelle ich den zugehörigen Code hier ein.

aXied
Beiträge: 20
Registriert: Mo 30. Sep 2013, 20:57

Re: XML und die Verwendung von XPath

Beitrag von aXied »

Nachdem ich nun mit meiner Umstellung fast fertig bin, habe ich bei Tests noch einen kleinen Fehler in socke's Code entdeckt.
Diesen möchte ich wie versprochen korrigiert weitergeben.

Das Problem in der Funktion GetPosition ist/war, gibt es kein vorheriges Geschwister wurde als Position 0 zurück geliefert.

Falscher Code - Korrigierter Code

Code: Alles auswählen

 
    // Position des Knotens in Bezug auf gleichnamige Geschwisterknoten
    function GetPosition(aNode: TDOMNode): Integer;
    var
      nn: DOMString;
    begin
      nn := anode.NodeName;
      [b][color=red]Result := 0;[/color][/b]
      [b][color=green]Result := 1;  //Die Zählung beginnt immer mit 1![/color][/b]
      while assigned(aNode.PreviousSibling) do
      begin
        [b][color=green]aNode := aNode.PreviousSibling;[/color][/b]
        if aNode.NodeName = nn then
          inc(Result);
        [b][color=red]aNode := aNode.PreviousSibling;[/color][/b]
      end;
    end;
 

Antworten