Difference between revisions of "Streaming JSON/de"
(3 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
{{Streaming JSON}} | {{Streaming JSON}} | ||
− | [[JSON]] (JavaScript Object Notation) ist ein textbasiertes, standardisiertes Datenformat. Wie der Name sagt, sind JSON-Dokumente gültiger JavaScript-Code und können | + | [[JSON]] (JavaScript Object Notation) ist ein textbasiertes, standardisiertes Datenformat. Wie der Name sagt, sind JSON-Dokumente gültiger JavaScript-Code und können dort direkt in Objekte umgesetzt werden. An und für sich kann es aber zum Datenaustausch unabhängig von der verwendeten Programmiersprache verwendet werden. |
In diesem Tutorial wird erklärt, wie man JSON-Daten in ein Free-Pascal-Programm lädt um sie dort zu verarbeiten; Ebenso wird erklärt, wie man die Daten aus dem Programm in JSON konvertiert (um sie dann zum Beispiel an einen Webbrowser zu senden). | In diesem Tutorial wird erklärt, wie man JSON-Daten in ein Free-Pascal-Programm lädt um sie dort zu verarbeiten; Ebenso wird erklärt, wie man die Daten aus dem Programm in JSON konvertiert (um sie dann zum Beispiel an einen Webbrowser zu senden). | ||
Line 8: | Line 8: | ||
Das Laden und Speichern von Objekten erfolgt mit der Unit [[fpjsonrtti]]. Weiterhin ist die Unit Classes sinnvoll (mehr dazu weiter unten im Abschnitt [[#Datenstruktur|Datenstruktur ?]]; Die [[Uses/de|uses]]-Anweisung sollte also mindestens diese beiden Units enthalten: | Das Laden und Speichern von Objekten erfolgt mit der Unit [[fpjsonrtti]]. Weiterhin ist die Unit Classes sinnvoll (mehr dazu weiter unten im Abschnitt [[#Datenstruktur|Datenstruktur ?]]; Die [[Uses/de|uses]]-Anweisung sollte also mindestens diese beiden Units enthalten: | ||
− | <syntaxhighlight>uses Classes, fpjsonrtti;</syntaxhighlight> | + | <syntaxhighlight lang=pascal>uses Classes, fpjsonrtti;</syntaxhighlight> |
Line 14: | Line 14: | ||
* JSON-Daten sind abhängig von Groß- und Kleinschreibung (case insensitive).[[#ref1|<sup>1</sup>]] Daraus folgt, dass die Eigenschaften der Free-Pascal-Objekte genau wie die JSON-Eigenschaften geschrieben werden müssen. | * JSON-Daten sind abhängig von Groß- und Kleinschreibung (case insensitive).[[#ref1|<sup>1</sup>]] Daraus folgt, dass die Eigenschaften der Free-Pascal-Objekte genau wie die JSON-Eigenschaften geschrieben werden müssen. | ||
* Es können keine Eigenschaften mit DefineProperties definiert werden; es können keine Referenzen auf Methoden (Event-Handler) gespeichert werden.[[#ref2|<sup>2</sup>]] | * Es können keine Eigenschaften mit DefineProperties definiert werden; es können keine Referenzen auf Methoden (Event-Handler) gespeichert werden.[[#ref2|<sup>2</sup>]] | ||
− | * [[TCollection]] und [[TStrings]] | + | * [[TCollection]] und [[TStrings]] können als Ersatz für Arrays verwendet werden. |
Mit den Quelltexten des Free-Pascal-Compilers wird im Verzeichnis <tt><fpc-quellen>/packages/fcl-json/examples/demortti.pp</tt> ein Demoprogramm mitgeliefert. | Mit den Quelltexten des Free-Pascal-Compilers wird im Verzeichnis <tt><fpc-quellen>/packages/fcl-json/examples/demortti.pp</tt> ein Demoprogramm mitgeliefert. | ||
Line 29: | Line 29: | ||
Im eigenen Programmquelltext kann sie so definiert werden: | Im eigenen Programmquelltext kann sie so definiert werden: | ||
− | <syntaxhighlight> | + | <syntaxhighlight lang=pascal> |
const JSON_TESTDATA = | const JSON_TESTDATA = | ||
'{'+LineEnding+ | '{'+LineEnding+ | ||
Line 41: | Line 41: | ||
== Datenstruktur == | == Datenstruktur == | ||
− | Als Basisklasse für die Daten bietet sich [[TPersistent]] aus der Unit Classes an, da für sie und alle Unterklassen [[Runtime Type Information (RTTI)|Laufzeit-Typinformationen]] (<abbr title="Runtime Type Information">RTTI</abbr>) erstellt werden. Diese werden für das Streaming zwingend benötigt. Da sich ''fpjsonrtti'' nicht in das Streaming-System integriert, kann auch jede andere Klasse, die mit dem Compilerschalter <syntaxhighlight | + | Als Basisklasse für die Daten bietet sich [[TPersistent]] aus der Unit Classes an, da für sie und alle Unterklassen [[Runtime Type Information (RTTI)|Laufzeit-Typinformationen]] (<abbr title="Runtime Type Information">RTTI</abbr>) erstellt werden. Diese werden für das Streaming zwingend benötigt. Da sich ''fpjsonrtti'' nicht in das Streaming-System integriert, kann auch jede andere Klasse, die mit dem Compilerschalter <syntaxhighlight inline lang=pascal>{$M+}</syntaxhighlight> übersetzt wurde, verwendet werden. |
Alle zu lesenden Eigenschaften müssen als [[Property/de|property]] im [[Published/de|published]]-Abschnitt der Klasse deklariert werden. In der Regel, kann hier mit read und write direkt auf ein Datenfeld (die Variable) verweisen werden. Wenn man möchte, können natürlich auch Getter- und Setter-Methoden verwendet werden. | Alle zu lesenden Eigenschaften müssen als [[Property/de|property]] im [[Published/de|published]]-Abschnitt der Klasse deklariert werden. In der Regel, kann hier mit read und write direkt auf ein Datenfeld (die Variable) verweisen werden. Wenn man möchte, können natürlich auch Getter- und Setter-Methoden verwendet werden. | ||
Line 47: | Line 47: | ||
Aus der JSON-Struktur ergibt sich folgende Klassendefinition. | Aus der JSON-Struktur ergibt sich folgende Klassendefinition. | ||
− | <syntaxhighlight> | + | <syntaxhighlight lang=pascal> |
type | type | ||
TNameObject = class(TCollectionItem) // Klasse für die Eigenschaft 'obj' und die TCollection | TNameObject = class(TCollectionItem) // Klasse für die Eigenschaft 'obj' und die TCollection | ||
Line 73: | Line 73: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | Die Klasse <syntaxhighlight | + | Die Klasse <syntaxhighlight inline lang=pascal>TNameObject</syntaxhighlight> wurde von [[TCollectionItem]] abgeleitet. Damit kann sie sowohl für die Eigenschaft ''obj'' als auch in der Collection verwendet werden. Wenn dies nicht gewünscht ist, müssen hier zwei unterschiedliche Klassen definiert werden. |
Im Konstruktor der Klasse TBaseObject müssen die TCollection sowie die String-Liste erstellt und im Destruktor wieder freigegeben werden. | Im Konstruktor der Klasse TBaseObject müssen die TCollection sowie die String-Liste erstellt und im Destruktor wieder freigegeben werden. | ||
− | <syntaxhighlight> | + | <syntaxhighlight lang=pascal> |
constructor TBaseObject.Create; | constructor TBaseObject.Create; | ||
begin | begin | ||
Line 100: | Line 100: | ||
== JSON laden == | == JSON laden == | ||
− | Mit der Methode <syntaxhighlight | + | Mit der Methode <syntaxhighlight inline lang=pascal>Procedure JSONToObject(Const JSON : TJSONStringType; AObject : TObject);</syntaxhighlight> der Klasse [[TJSONDeStreamer]] können Sie JSON-Daten direkt an ein ''vorhandenes'' Objekt zuweisen. Bevor Sie die Methode aufrufen, müssen Sie TJSONDeStreamer und das Ziel-Objekt erstellen. |
− | Die folgende Methode lädt die Daten aus der JSON-Struktur <syntaxhighlight | + | Die folgende Methode lädt die Daten aus der JSON-Struktur <syntaxhighlight inline lang=pascal>JSON_TESTDATA</syntaxhighlight> in das Objekt o und gibt die aktuellen Werte der Eigenschaften danach auf der Konsole aus. |
− | <syntaxhighlight> | + | <syntaxhighlight lang=pascal> |
procedure DeStreamTest; | procedure DeStreamTest; | ||
var | var | ||
Line 142: | Line 142: | ||
== JSON speichern == | == JSON speichern == | ||
− | Um ein Objekt in einen JSON-Text zu überführen, wird die Klasse [[TJSONStreamer]] verwendet. Hier wird die Methode <syntaxhighlight | + | Um ein Objekt in einen JSON-Text zu überführen, wird die Klasse [[TJSONStreamer]] verwendet. Hier wird die Methode <syntaxhighlight inline lang=pascal>Function ObjectToJSONString(AObject : TObject) : TJSONStringType;</syntaxhighlight> verwendet. |
In der folgenden Prozedur wird ein Objekt erstellt, mit den Testdaten befüllt und anschließend nach JSON überführt. Der JSON-Text wird auf der Konsole ausgegeben. Es kann nicht festgelegt werden, in welcher Reihenfolge die Eigenschaften ausgeben werden | In der folgenden Prozedur wird ein Objekt erstellt, mit den Testdaten befüllt und anschließend nach JSON überführt. Der JSON-Text wird auf der Konsole ausgegeben. Es kann nicht festgelegt werden, in welcher Reihenfolge die Eigenschaften ausgeben werden | ||
− | <syntaxhighlight> | + | <syntaxhighlight lang=pascal> |
procedure StreamTest; | procedure StreamTest; | ||
var | var | ||
Line 187: | Line 187: | ||
== Siehe auch == | == Siehe auch == | ||
+ | |||
* [[JSON]] | * [[JSON]] | ||
* [[fcl-json]] | * [[fcl-json]] | ||
Line 192: | Line 193: | ||
== Einzelnachweise == | == Einzelnachweise == | ||
+ | |||
# <div id="ref1">http://lists.freepascal.org/fpc-pascal/2013-January/036254.html</div> | # <div id="ref1">http://lists.freepascal.org/fpc-pascal/2013-January/036254.html</div> | ||
# <div id="ref2>http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-January/058878.html</div> | # <div id="ref2>http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-January/058878.html</div> | ||
− | |||
− | |||
− | |||
− |
Latest revision as of 17:13, 6 August 2022
│
Deutsch (de) │
English (en) │
polski (pl) │
русский (ru) │
中文(中国大陆) (zh_CN) │
JSON (JavaScript Object Notation) ist ein textbasiertes, standardisiertes Datenformat. Wie der Name sagt, sind JSON-Dokumente gültiger JavaScript-Code und können dort direkt in Objekte umgesetzt werden. An und für sich kann es aber zum Datenaustausch unabhängig von der verwendeten Programmiersprache verwendet werden.
In diesem Tutorial wird erklärt, wie man JSON-Daten in ein Free-Pascal-Programm lädt um sie dort zu verarbeiten; Ebenso wird erklärt, wie man die Daten aus dem Programm in JSON konvertiert (um sie dann zum Beispiel an einen Webbrowser zu senden).
Allgemeines und Voraussetzungen
Das Laden und Speichern von Objekten erfolgt mit der Unit fpjsonrtti. Weiterhin ist die Unit Classes sinnvoll (mehr dazu weiter unten im Abschnitt Datenstruktur ?; Die uses-Anweisung sollte also mindestens diese beiden Units enthalten:
uses Classes, fpjsonrtti;
Zum derzeitigen Zeitpunkt (Mai 2014) gibt es einige Unterschiede zum Streaming-Systems von Free Pascal:
- JSON-Daten sind abhängig von Groß- und Kleinschreibung (case insensitive).1 Daraus folgt, dass die Eigenschaften der Free-Pascal-Objekte genau wie die JSON-Eigenschaften geschrieben werden müssen.
- Es können keine Eigenschaften mit DefineProperties definiert werden; es können keine Referenzen auf Methoden (Event-Handler) gespeichert werden.2
- TCollection und TStrings können als Ersatz für Arrays verwendet werden.
Mit den Quelltexten des Free-Pascal-Compilers wird im Verzeichnis <fpc-quellen>/packages/fcl-json/examples/demortti.pp ein Demoprogramm mitgeliefert.
Als Beispiel wird im Folgenden immer diese JSON-Struktur verwendet.
{
"id" : 123, // ein Integer
"obj" : { "name": "Hallo Welt!" }, // ein Objekt
"coll" : [ { "name": "Objekt 1" }, { "name": "Object 2" } ], // zwei Objekte in einer TCollection
"strings": [ "Hallo 1", "Hallo 2" ] // eine String-Liste
}
Im eigenen Programmquelltext kann sie so definiert werden:
const JSON_TESTDATA =
'{'+LineEnding+
' "id": 123,'+LineEnding+
' "obj": { "name": "Hallo Welt!" },'+LineEnding+
' "coll": [ { "name": "Objekt 1" }, { "name": "Objekt 2" } ],'+LineEnding+
' "strings": [ "Hallo 1", "Hallo 2" ]'+LineEnding+
'}';
Datenstruktur
Als Basisklasse für die Daten bietet sich TPersistent aus der Unit Classes an, da für sie und alle Unterklassen Laufzeit-Typinformationen (RTTI) erstellt werden. Diese werden für das Streaming zwingend benötigt. Da sich fpjsonrtti nicht in das Streaming-System integriert, kann auch jede andere Klasse, die mit dem Compilerschalter {$M+}
übersetzt wurde, verwendet werden.
Alle zu lesenden Eigenschaften müssen als property im published-Abschnitt der Klasse deklariert werden. In der Regel, kann hier mit read und write direkt auf ein Datenfeld (die Variable) verweisen werden. Wenn man möchte, können natürlich auch Getter- und Setter-Methoden verwendet werden.
Aus der JSON-Struktur ergibt sich folgende Klassendefinition.
type
TNameObject = class(TCollectionItem) // Klasse für die Eigenschaft 'obj' und die TCollection
private
fName: String;
published
property name: String read fName write fName;
end;
TBaseObject = class(TPersistent) // Klasse für die gesamte JSON-Struktur
private
fid: Integer;
fObj: TNameObject;
fColl: TCollection;
fStrings: TStrings;
public
constructor Create;
destructor Destroy; override;
published // alle Eigenschaften müssen published sein
property id: Integer read fid write fid;
property obj: TNameObject read fObj write fObj;
property coll: TCollection read fColl;
property strings: TStrings read fStrings;
end;
Die Klasse TNameObject
wurde von TCollectionItem abgeleitet. Damit kann sie sowohl für die Eigenschaft obj als auch in der Collection verwendet werden. Wenn dies nicht gewünscht ist, müssen hier zwei unterschiedliche Klassen definiert werden.
Im Konstruktor der Klasse TBaseObject müssen die TCollection sowie die String-Liste erstellt und im Destruktor wieder freigegeben werden.
constructor TBaseObject.Create;
begin
// Collection und StringList erstellen
fColl := TCollection.Create(TNameObject);
fStrings := TStringList.Create;
fObj := TNameObject.Create(nil);
end;
destructor TBaseObject.Destroy;
begin
// Collection und StringList wieder freigeben
fColl.Free;
fStrings.Free;
fObj.Free;
inherited Destroy;
end;
Sofern Sie keine weitere Funktionalität in den Datenklassen haben möchten, ist ihre Definition bereits fertig.
JSON laden
Mit der Methode Procedure JSONToObject(Const JSON : TJSONStringType; AObject : TObject);
der Klasse TJSONDeStreamer können Sie JSON-Daten direkt an ein vorhandenes Objekt zuweisen. Bevor Sie die Methode aufrufen, müssen Sie TJSONDeStreamer und das Ziel-Objekt erstellen.
Die folgende Methode lädt die Daten aus der JSON-Struktur JSON_TESTDATA
in das Objekt o und gibt die aktuellen Werte der Eigenschaften danach auf der Konsole aus.
procedure DeStreamTest;
var
DeStreamer: TJSONDeStreamer;
o: TBaseObject;
no: TNameObject;
s: String;
begin
WriteLn('DeStream test');
WriteLn('======================================');
// DeStreamer-Objekt und Ziel-Objekt erstellen
DeStreamer := TJSONDeStreamer.Create(nil);
o := TBaseObject.Create;
try
// JSON-Daten in das Objekt o laden
DeStreamer.JSONToObject(JSON_TESTDATA, o);
// ID ausgeben
WriteLn(o.id);
// Objekt-Name ausgeben
WriteLn(o.obj.name);
// alle Namen der Objekte ausgeben
for TCollectionItem(no) in o.coll do
Writeln(no.name);
// alle Strings ausgeben
for s in o.strings do
WriteLn(s);
// aufräumen
finally
o.Destroy;
DeStreamer.Destroy;
end;
end;
JSON speichern
Um ein Objekt in einen JSON-Text zu überführen, wird die Klasse TJSONStreamer verwendet. Hier wird die Methode Function ObjectToJSONString(AObject : TObject) : TJSONStringType;
verwendet.
In der folgenden Prozedur wird ein Objekt erstellt, mit den Testdaten befüllt und anschließend nach JSON überführt. Der JSON-Text wird auf der Konsole ausgegeben. Es kann nicht festgelegt werden, in welcher Reihenfolge die Eigenschaften ausgeben werden
procedure StreamTest;
var
Streamer: TJSONStreamer;
o: TBaseObject;
JSONString: String;
begin
WriteLn('Stream test');
WriteLn('======================================');
Streamer := TJSONStreamer.Create(nil);
o := TBaseObject.Create;
try
// Daten festlegen
o.id := 123;
o.obj.name := 'Hallo Welt!';
TNameObject(o.coll.Add).name := 'Objekt 1';
TNameObject(o.coll.Add).name := 'Objekt 2';
o.strings.Add('Hallo 1');
o.strings.Add('Hallo 2');
Streamer.Options := Streamer.Options + [jsoTStringsAsArray]; // Strings als JSON-Array ausgeben
// nach JSON überführen und ausgeben
JSONString := Streamer.ObjectToJSONString(o);
WriteLn(JSONString);
// aufräumen
finally
o.Destroy;
Streamer.Destroy;
end;
end;
Ausblick
Mit dem vorgestellten Wissen können einfache und komplexe JSON-Datenstrukturen in Free Pascal geladen werden. Sollte eine Vor- oder Nachbearbeitung der JSON-Daten notwendig sein, können die Textdaten zunächst mit der Klasse TJSONParser aus der Unit jsonparser in eine JSON-Datenstruktur geladen werden und dann mit der Unit fpJSON beliebig manipuliert werden.
Über die Eigenschaft Options der Klasse TJSONStreamer kann gesteuert werden, wie die Ausgabe die eigenen Datenstrukturen in JSON abgebildet werden.