Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Erzeugen und Freigeben von TJSONTextWriter wirklich so umständlich? (https://www.delphipraxis.net/199794-erzeugen-und-freigeben-von-tjsontextwriter-wirklich-so-umstaendlich.html)

Bbommel 20. Feb 2019 09:05

Erzeugen und Freigeben von TJSONTextWriter wirklich so umständlich?
 
Hallo zusammen,

zur Kommunikation zwischen einem Server und einem Client nutze ich an diversen Stellen JSON. Der Code, um irgendeine Info als JSON-String zu erzeugen, sah dabei meistens in etwa so aus:
Delphi-Quellcode:
function TmyClass.GetInfoAsJSON: string;

var
  jWriter: TJsonTextWriter;

begin
  jWriter:=TJsonTextWriter.Create(TStringWriter.Create);
  try
    jWriter.WriteStartObject;
    jWriter.WritePropertyName('values');
    jWriter.WriteValue('wuppdi');
    jWriter.WriteEndObject;
    Result:=jWriter.Writer.ToString;
  finally
    jWriter.Writer.Free;
    jWriter.Free;
  end;
end;
(natürlich sind die erzeugten JSON-Strukturen normalerweise etwas komplexer :-) )

Funktioniert auch soweit, dachte ich zumindest. Im Client kam immer gültiges JSON an und konnte dort problemlos verarbeitet werden. Nachdem ich das jetzt schon einige Zeit so nutze, habe ich aber gestern gemerkt, dass der Server ab und zu mit einer AV abschmiert. Nach diversen grauen Haaren mehr kam ich dann darauf, dass die Ursache tatsächlich das Freigeben des "jWriter" ist, also der Aufruf "jWriter.Free".

Ursache ist, dass innerhalb des Destroy vom TJSONTextWriter noch einmal auf den TextWriter innerhalb des JSONTextWriter zugegriffen wird, also auf TJSONTextWriter.FWriter. Nur: den habe ich ja schon in der Zeile dadrüber mit "jWriter.Writer.Free" freigegeben - muss ja auch weg. Die Lösung ist also, dass man sich diesen inneren TextWriter entweder beim Erzeugen oder beim Freigeben in einer eigenen Variablen merkt.

Der Code sieht dann also so aus:
Delphi-Quellcode:
function TmyClass.GetInfoAsJSON: string;

var
  jWriter: TJsonTextWriter;
  textWriter: TTextWriter;

begin
  jWriter:=TJsonTextWriter.Create(TStringWriter.Create);
  try
    jWriter.WriteStartObject;
    jWriter.WritePropertyName('values');
    jWriter.WriteValue('wuppdi');
    jWriter.WriteEndObject;
    Result:=jWriter.Writer.ToString;
  finally
    textWriter:=jWriter.Writer;
    jWriter.Free;
    textWriter.Free;
  end;
end;
Wie gesagt, man kann sich den TextWriter natürlich auch oben beim Create merken - ich hab es jetzt beim Freigeben gemacht, um das Erezugen und Freigeben dann leichter in zwei kleine Hilfsfunktionen auslagern zu können. So funktioniert jetzt auch alles wieder ohne Zugriffsverletzung.

Mir kommt das aber irgendwie alles ziemlich umständlich vor. Obwohl ich schon in Delphi gesucht und nichts anderes gefunden habe und ich mir daher ziemlich sicher bin, das richtig zu nutzen, dennoch mal die Frage: mir kommt das sehr merkwürdig vor, dass man so viele Zeilen braucht, um das Ding ordentlich zu erzeugen und freizugeben. Ist das wirklich so gedacht oder übersehe ich da irgendwo eine viel einfachere Struktur, die man eigentlich benutzen sollte?

Falls das alles tatsächlich so gedacht ist, dann möge dieser Post zukünftigen Lesern zumindest als Hilfe dienen, nicht über den gleichen Fehler zu stolpern. :-) Und vielleicht wäre es ja eine Idee, dass man dem TJSONTextWriter beim Create mitgeben könnte, dass er der Owner vom inneren Writer ist, dann könnte er das Dingen beim Zerstören selber in der richtigen Reihenfolge freigeben und man hätte das ganze Generve nicht.

Schokohase 20. Feb 2019 09:11

AW: Erzeugen und Freigeben von TJSONTextWriter wirklich so umständlich?
 
So würde man es ja normalerweise machen:
Delphi-Quellcode:
function TmyClass.GetInfoAsJSON: string;
var
  jWriter: TJsonTextWriter;
  textWriter: TTextWriter;
begin
  textWriter := TStringWriter.Create;
  try
    jWriter:=TJsonTextWriter.Create(textWriter);
    try
      jWriter.WriteStartObject;
      jWriter.WritePropertyName('values');
      jWriter.WriteValue('wuppdi');
      jWriter.WriteEndObject;
      Result:=jWriter.Writer.ToString;
    finally
      jWriter.Free;
    end;
  finally
    textWriter.Free;
  end;
end;
oder alternativ auch so
Delphi-Quellcode:
function TmyClass.GetInfoAsJSON: string;
var
  jWriter: TJsonTextWriter;
  textWriter: TTextWriter;
begin
  jWriter := nil;
  textWriter := TStringWriter.Create;
  try
    jWriter:=TJsonTextWriter.Create(textWriter);
    jWriter.WriteStartObject;
    jWriter.WritePropertyName('values');
    jWriter.WriteValue('wuppdi');
    jWriter.WriteEndObject;
    Result:=jWriter.Writer.ToString;
  finally
    jWriter.Free;
    textWriter.Free;
  end;
end;

Bbommel 20. Feb 2019 09:17

AW: Erzeugen und Freigeben von TJSONTextWriter wirklich so umständlich?
 
Danke für die Rückmeldung, Schokohase. Das war das, was ich in meinem Beitrag mit "man kann sich den TextWriter auch beim Create merken" meinte - ist sicherlich die erstmal schönere Variante. Ich hatte halt jetzt schon soweit gebastelt, um dieses ganze Erzeugen und Freigaben nachher in Hilfsfunktionen (oder eine abgeleitete Klasse, mal sehen) auslagern zu können.

Aber meine eigentliche Frage hast du ja dann bestätigt: ja, es ist so, dass man immer beide Objekte braucht und tunlichst in der richtigen Reihenfolge freigeben muss. :-)

Schokohase 20. Feb 2019 09:37

AW: Erzeugen und Freigeben von TJSONTextWriter wirklich so umständlich?
 
Ich würde den Code sogar noch etwas weiter aufdröseln zu
Delphi-Quellcode:
function TmyClass.GetInfoAsJSON: string;
var
  jWriter: TJsonTextWriter;
  textWriter: TTextWriter;
begin
  textWriter := TStringWriter.Create;
  try
    // start write json
    jWriter:=TJsonTextWriter.Create(textWriter);
    try
      jWriter.WriteStartObject;
      jWriter.WritePropertyName('values');
      jWriter.WriteValue('wuppdi');
      jWriter.WriteEndObject;
    finally
      jWriter.Free;
    end;
    // end write json
    Result := textWriter.ToString;
  finally
    textWriter.Free;
  end;
end;
und nun sieht man, dass man diesen JSON-Write Kram auch in eine eigene Methode auslagern kann.
Delphi-Quellcode:
procedure TmyClass.WriteJSONInfo(const AWriter: TTextWriter);
begin
  jWriter:=TJsonTextWriter.Create(AWriter);
  try
    jWriter.WriteStartObject;
    jWriter.WritePropertyName('values');
    jWriter.WriteValue('wuppdi');
    jWriter.WriteEndObject;
  finally
    jWriter.Free;
  end;
end;

function TmyClass.GetInfoAsJSON: string;
var
  textWriter: TTextWriter;
begin
  textWriter := TStringWriter.Create;
  try
    WriteJSONInfo(textWriter);
    Result := textWriter.ToString;
  finally
    textWriter.Free;
  end;
end;
und schon sieht das wesentlich entzerrter aus und man kann jetzt jeden beliebigen TextWriter von aussen übergeben (zum Testen, zum ...)


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