Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   GraphML, XML Unterschiede im Encoding ? (https://www.delphipraxis.net/210456-graphml-xml-unterschiede-im-encoding.html)

bernhard_LA 27. Apr 2022 23:01


GraphML, XML Unterschiede im Encoding ?
 
Liste der Anhänge anzeigen (Anzahl: 3)
ich schreibe mit diesem Code GraphML, XML Dateien :


Delphi-Quellcode:
var
  i, j: Integer;
const
  ncnt = 60;
var
  aXMLStreamWriter: TGRAPHMLStreamWriter;
  h1, h2: string;
begin
  aXMLStreamWriter := TGRAPHMLStreamWriter.Create;
  try

    aXMLStreamWriter.AddHeader;

    aXMLStreamWriter.StartTag('graph');
    aXMLStreamWriter.SetAttribute('id', 'G');
    aXMLStreamWriter.SetAttribute('edgedefault', 'undirected');

    for i := 0 to ncnt do
    begin
      aXMLStreamWriter.StartTag('node');
      aXMLStreamWriter.SetAttribute('id', 'n' + IntToStr(i));
      aXMLStreamWriter.StopTag;
    end;

    for i := 0 to ncnt do
    begin
      h1 := 'n' + IntToStr(i);
      h2 := 'n' + IntToStr( ( i +1 ) mod ncnt );
      aXMLStreamWriter.StartTag('edge');
      aXMLStreamWriter.SetAttribute('source', h1);
      aXMLStreamWriter.SetAttribute('target', h2);
      aXMLStreamWriter.StopTag;
    end;

    aXMLStreamWriter.StopTag;

    aXMLStreamWriter.AddFooter;

    aXMLStreamWriter.SavetoFile('e:\test.graphML');

  finally
    XMLMemo.Lines.Add(aXMLStreamWriter.ToString);

    XMLMemo.Lines.SavetoFile('e:\test2.graphML');
  end;

Nur die test2.graphML kann von der Software Graphia ( https://graphia.app/ ) eingelesen, die Datei test.graphML wird nicht als gültige XML Datei erkannt.

Ich vermute der Fehler liegt beim Encoding in meinem Stringstream, habe aber bereits hier UTF-8 ausgewählt.
Die Funktionen zum Schreiben der XML Files sind hier :
Das lauffähige Projekt und die Beipieldatein in in der Anlage.



Delphi-Quellcode:


type
  TXMLStreamWriter = class

  private

    /// <summary>
    /// end the current string/TAG with a new line
    /// </summary>
    FNewLineWritten: Boolean;

    FIsStartTagClosed: Boolean;

    FStream: TStringStream;

    FTagStack: TStringStack;
    /// <summary>
    /// original comment : not entirely correct implementation as some names
    /// are correct <br />but it still returns False, but that occurs only
    /// with names using <br />characters outside the american alphabet
    /// </summary>
    function IsValidTagName(const ATagName: WideString): Boolean;

    /// <summary>
    /// a too simple check for invalid characters, more or less correct for
    /// ANSI names.
    /// </summary>
    /// <remarks>
    /// [#0..#$40,#$5B..#$60,#$7B..#$FF]; <br />InvalidOtherChar =
    /// [#0..#$2C,#$5B..#$60,#$7B..#$B6,#$B8..#$FF]; <br />
    /// ValidNameOtherChar:WideString =
    /// ['a'..'z','A'..'Z','_',':','0'..'9','.','-'];
    /// </remarks>
    function IsValidAttributeName(const AAttributeName: WideString): Boolean;

    procedure Indent;

    function IsTagStarted: Boolean;

    function LastTag: String;

    function MakeValidAttributeValue(const AValue: String): String;

    procedure AssertLastTagClosed;

  public

    constructor Create;

    destructor Destroy; override;



    procedure AddRaw(const Data: String);

    procedure AddNewLine;

    procedure SetAttribute(const Attribute, Value: String);

    function ToString: String;

    procedure StartTag(const Tag: String);

    procedure StopTag;

    procedure SavetoFile(Filename : string);



  end;


type

  TGRAPHMLStreamWriter = class(TXMLStreamWriter)


  procedure AddHeader;

  procedure AddFooter;
  end;





implementation
{ TXMLStreamWriter }


function TXMLStreamWriter.IsValidTagName(const ATagName: WideString): Boolean;
begin
  Result := IsValidAttributeName(ATagName);
end;



procedure TXMLStreamWriter.SavetoFile(Filename: string);
begin
    FStream.SaveToFile(Filename);
end;


procedure TXMLStreamWriter.AddRaw(const Data: String);
var
  StrLen: integer;
begin
  // Stream.WriteBuffer(Pointer(Str)^, StrLen * SizeOf(Char)); //All Delphi versions compatible
  StrLen := length(Data);
  self.FStream.WriteBuffer(Pointer(Data)^, StrLen * SizeOf(Char))
end;

constructor TXMLStreamWriter.Create;
begin
  FTagStack := TStringStack.Create;

  FStream := TStringStream.Create('', TEncoding.UTF8);

  FIsStartTagClosed := True;
end;

destructor TXMLStreamWriter.Destroy;
begin
  FTagStack.Free;

  FStream.Free;

  inherited;
end;

function TXMLStreamWriter.ToString: String;
begin
  Result := self.FStream.DataString;
end;


{ TGRAPHMLStreamWriter }

procedure TGRAPHMLStreamWriter.AddFooter;
begin
    self.AddRaw('</graphml>');
end;

procedure TGRAPHMLStreamWriter.AddHeader;
begin
  self.AddRaw('<?xml version="1.0" encoding="UTF-8"?> ');
  self.AddRaw('<graphml xmlns="http://graphml.graphdrawing.org/xmlns" ');
  self.AddRaw('xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ');
  self.AddRaw('xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns ');
  self.AddRaw('http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> ');
  AddNewLine;
end;

Uwe Raabe 27. Apr 2022 23:21

AW: GraphML, XML Unterschiede im Encoding ?
 
Klappt dies:

Delphi-Quellcode:
...
XMLMemo.Lines.SavetoFile('e:\test2.graphML', TEncoding.UTF8);
...

bernhard_LA 28. Apr 2022 09:55

AW: GraphML, XML Unterschiede im Encoding ?
 
Liste der Anhänge anzeigen (Anzahl: 2)
@ Uwe

nur die test2.graphML kann bereits gelesen werden, Sie wird ja vom Display Memo in meiner demo Anwendung heraus geschrieben .
Der Fehler tritt bei test.graphML auf , diese Datei wird von meinem TStringStream heraus geschrieben,
und vermutlich wird bei der .AddRow( ...) Funktion immer ein Fehler im encoding gemacht. Mir fehlt aber das
Verständnis was nicht klappt .
Im Memo in der Anwendung sehen die Daten ja perfekt aus.





Die Graph Analysis Software GEPHI (https://gephi.org/) verwende ich zur Anzeige der Graphen, screen dump mit dem testgraphen :-)

Uwe Raabe 28. Apr 2022 10:04

AW: GraphML, XML Unterschiede im Encoding ?
 
Zitat:

Zitat von bernhard_LA (Beitrag 1505181)
die test2.graphML kann bereits gelesen werden, sie wird ja vom Display Memo in meiner demo Anwendung heraus geschrieben .
der Fehler tritt bei test.graphML auf , diese Datei wird von meinem TStringStream heraus geschrieben,

Gibt es denn irgendwo die Sourcen zu
Delphi-Quellcode:
TGRAPHMLStreamWriter
? Andernfalls endet das in einem wilden Raten.

bernhard_LA 28. Apr 2022 11:27

AW: GraphML, XML Unterschiede im Encoding ?
 
Liste der Anhänge anzeigen (Anzahl: 2)
hier noch die Sourcen einzeln , das ganze Projekt ist lauffähig

Uwe Raabe 28. Apr 2022 12:08

AW: GraphML, XML Unterschiede im Encoding ?
 
Zitat:

Zitat von bernhard_LA (Beitrag 1505190)
das ganze Projekt ist lauffähig

Nicht ganz - es fehlt noch diese Unit: C:\repos\let\codes\code_minor_subprojects\OTHERS software and code testing tools\XML_StreamWriter\Unit_XMLStreamWriter.pas

Uwe Raabe 28. Apr 2022 12:13

AW: GraphML, XML Unterschiede im Encoding ?
 
Ich glaube es liegt an AddRaw. Dort wird die Binärdarstellung (UTF-16) des strings in den Stream geschrieben, was das angegebene Encoding außer Kraft setzt. So sollte es gehen:
Delphi-Quellcode:
procedure TXMLStreamWriter.AddRaw(const Data: String);
begin
  FStream.WriteString(Data);
end;

bernhard_LA 28. Apr 2022 17:47

AW: GraphML, XML Unterschiede im Encoding ?
 
@Uwe : Danke für die Fehler Suche, Problem ist jetzt gelöst. Ich lade den fertigen Code zum schnellen Schreiben von XML und GraphML File demnächst hier oder auf Github hoch , könnte ja noch mehrere Anwender finden :-)

jus 28. Apr 2022 18:14

AW: GraphML, XML Unterschiede im Encoding ?
 
Zitat:

Zitat von bernhard_LA (Beitrag 1505169)
ich schreibe mit diesem Code GraphML, XML Dateien :

Delphi-Quellcode:

{ TGRAPHMLStreamWriter }

procedure TGRAPHMLStreamWriter.AddFooter;
begin
    self.AddRaw('</graphml>');
end;

procedure TGRAPHMLStreamWriter.AddHeader;
begin
  self.AddRaw('<?xml version="1.0" encoding="UTF-8"?> ');
  self.AddRaw('<graphml xmlns="http://graphml.graphdrawing.org/xmlns" ');
  self.AddRaw('xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ');
  self.AddRaw('xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns ');
  self.AddRaw('http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> ');
  AddNewLine;
end;

Da ich bisher nur mit DOM gearbeitet habe, hat mich der SAX Ansatz auch sehr interessiert, darum habe ich den Code von Marabu auch gleich ausprobiert. Heute am Abend möchte das Einlesen mit SAX ausprobieren. Aber was mir aufgefallen ist, dass du eigentlich die folgende Zeile
Delphi-Quellcode:
self.AddRaw('<?xml version="1.0" encoding="UTF-8"?> ');
nicht wirklich brauchst oder? Die wird bei mir automatisch erzeugt, wenn ich den Marabu Code verwende. Außerdem, hast du dir den folgenden Codeschnipsel im selben verlinkten Marabu Beitrag angeschaut?
Delphi-Quellcode:
begin
  // ...
  fn := 'c:\daten\dp\sax-demo.xml';
  fs := TFileStream.Create(fn, fmCreate);
  wrt := CoMXXMLWriter.Create;
  cnth := wrt as ISAXContentHandler;
  wrt.output := TStreamAdapter.Create(fs, soReference) as IStream;
  cnth.startDocument;
  dc.Serialize(cnth);
  cnth.endDocument;
  wrt.flush;
  fs.Free;
  // ...
end;
Hier zeigt der Marabu wie man anstatt von TMemo Feld das Ergebnis direkt in eine Datei umleiten kann. Damit spart man sich auch Probleme beim Konvertieren. Es funktionierte bei mir auf Anhieb.

Edit:
Ok, Asche auf mein Haupt :duck: ich habe da wohl ein Blödsinn bzgl. UTF8 Formatierung erzählt. Automatisch wird bei mir UTF16 von MSXML verwendet. Ich bleibe aber dabei, dass man die Codierung lieber dem MSXML überlassen sollte statt selber reinzupfuschen. Mit folgenden Code kann man die Codierung beeinflussen:
Delphi-Quellcode:
begin
  // ...
  fn := 'c:\daten\dp\sax-demo.xml';
  fs := TFileStream.Create(fn, fmCreate);
  wrt := CoMXXMLWriter.Create;
  cnth := wrt as ISAXContentHandler;
  wrt.output := TStreamAdapter.Create(fs, soReference) as IStream;

  wrt.encoding := 'utf-8';  // <-----------------standardmäßig geht er auf UTF-16

  cnth.startDocument;
  dc.Serialize(cnth);
  cnth.endDocument;
  wrt.flush;
  fs.Free;
  // ...
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:04 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