Difference between revisions of "Streaming JSON"

From Lazarus wiki
Jump to navigationJump to search
(categorization)
(Some language fixes: this really wasn't English/comprehensible without the original German. Don't use <source> but <syntaxhighlight>)
Line 1: Line 1:
 
{{Translate}}
 
{{Translate}}
  
note: This article was translated from [[Streaming_JSON/de]] German on 20141002 using Google Translate. Minor edits have been made.  
+
note: This article was originally translated from [[Streaming_JSON/de]] German on 20141002 using Google Translat.
  
[[JSON]] (JavaScript Object Notation) is a text-based, standardized data format. As the name implies, JSON documents are valid JavaScript code where they can be directly converted into objects there. But in and of itself can be used regardless of the programming language used for data exchange.
+
[[JSON]] (JavaScript Object Notation) is a text-based, standardized data format. As the name implies, JSON documents are valid JavaScript code where they can be directly converted into JavaScript objects. But in and of itself it can be used regardless of the programming language used for data exchange.
 
 
This tutorial explains how to load JSON data in a Free-Pascal program to process them there; It also explains how to convert the data from the program in JSON (to send it then for example to a web browser).
 
  
 +
This tutorial explains how to load JSON data in a Free Pascal program to process it. It also explains how to convert FPC data into JSON (e.g. to send it to a web server).
  
 
== General and conditions ==
 
== General and conditions ==
Loading and storing objects is done with the Unit fpjsonrtti . Furthermore, the Classes unit is useful (more on this below in the section data structure ⇓ , The uses statement should therefore contain at least these two units:
+
Loading and storing (streaming) FPC object data is done with the fpjsonrtti unit. Furthermore, the Classes unit is useful (see below for details).
<source>uses Classes, fpjsonrtti;</source>
 
 
 
  
 +
The uses statement should therefore contain at least these two units:
 +
<syntaxhighlight>uses Classes, fpjsonrtti;</syntaxhighlight>
  
At the present time (May 2014) there are some differences to the streaming system of Free Pascal:
+
Currently (May 2014) there are some differences to the streaming system of Free Pascal with ??presumably JavaScript??:
* JSON data are case-sensitive (case insensitive). 1 It follows that the properties of the Free Pascal objects as JSON properties must be written exactly.
+
* JSON data is case-sensitive. Therefore, the properties of Free Pascal objects as JSON properties must be written with correct casing.
* It can be defined with DefineProperties no properties; it can be stored on methods (event handlers) no references. [[#ref2|<sup>2</sup>]]
+
* No properties can be defined with DefineProperties. Neither can references to methods (event handlers) be stored. [[#ref2|<sup>2</sup>]]
 
* [[TCollection]] and [[TStrings]] could be used as a substitute for arrays.
 
* [[TCollection]] and [[TStrings]] could be used as a substitute for arrays.
  
Demo programs are provided with the source code of Free Pascal Compiler. On linux this is typically <tt>/usr/share/fpc/{vers}/packages/fcl-json/examples</tt>.  
+
Demo programs are provided with the source code of Free Pascal Compiler (in the packages/fcl-json/examples directory).
  
 
As an example, we will start with the following JSON data:
 
As an example, we will start with the following JSON data:
  
<source lang="JavaScript">
+
<syntaxhighlight lang="JavaScript">
 
{
 
{
 
   "id"    : 123,                                                // an Integer
 
   "id"    : 123,                                                // an Integer
   "obj"    : { "name": "Hallo Welt!" },                          // an Object
+
   "obj"    : { "name": "Hello world!" },                          // an Object
   "coll"  : [ { "name": "Objekt 1" }, { "name": "Object 2" } ], // two objects in a TCollection  
+
   "coll"  : [ { "name": "Object 1" }, { "name": "Object 2" } ], // two objects in a TCollection  
   "strings": [ "Hallo 1", "Hallo 2" ]                            // a string list
+
   "strings": [ "Hallo 1", "Hello 2" ]                            // a string list
 
}
 
}
</source>
+
</syntaxhighlight>
  
 
This can be assigned in FreePascal using a constant assignment as follows:
 
This can be assigned in FreePascal using a constant assignment as follows:
<source>
+
<syntaxhighlight>
 
const JSON_TESTDATA =
 
const JSON_TESTDATA =
 
'{'+LineEnding+
 
'{'+LineEnding+
 
'  "id": 123,'+LineEnding+
 
'  "id": 123,'+LineEnding+
'  "obj": { "name": "Hallo Welt!" },'+LineEnding+
+
'  "obj": { "name": "Hello world!" },'+LineEnding+
'  "coll": [ { "name": "Objekt 1" }, { "name": "Objekt 2" } ],'+LineEnding+
+
'  "coll": [ { "name": "Object 1" }, { "name": "Object 2" } ],'+LineEnding+
'  "strings": [ "Hallo 1", "Hallo 2" ]'+LineEnding+
+
'  "strings": [ "Hello 1", "Hello 2" ]'+LineEnding+
 
'}';
 
'}';
</source>
+
</syntaxhighlight>
  
 
== Data Structure ==
 
== Data Structure ==
Line 51: Line 50:
 
From the JSON structure results in the following class definition.
 
From the JSON structure results in the following class definition.
  
<source>
+
<syntaxhighlight>
 
type
 
type
 
   TNameObject = class(TCollectionItem) // class for the property 'obj' and the TCollection  
 
   TNameObject = class(TCollectionItem) // class for the property 'obj' and the TCollection  
Line 75: Line 74:
 
     property strings: TStrings read fStrings;
 
     property strings: TStrings read fStrings;
 
   end;  
 
   end;  
</source>
+
</syntaxhighlight>
  
 
The class <source enclose="none">TNameObject</source> is of [[TCollectionItem]] derived. So they can both for the property ''obj'' be used as well in the collection. If this is not desired, here two different classes must be defined.
 
The class <source enclose="none">TNameObject</source> is of [[TCollectionItem]] derived. So they can both for the property ''obj'' be used as well in the collection. If this is not desired, here two different classes must be defined.
Line 81: Line 80:
 
In the constructor of the class TBaseObject the TCollection and the string list must be created and released in the destructor.
 
In the constructor of the class TBaseObject the TCollection and the string list must be created and released in the destructor.
  
<source>
+
<syntaxhighlight>
 
constructor TBaseObject.Create;
 
constructor TBaseObject.Create;
 
begin
 
begin
Line 98: Line 97:
 
   inherited Destroy;
 
   inherited Destroy;
 
end;
 
end;
</source>
+
</syntaxhighlight>
  
 
If you do not want any more functionality in the data classes, their definition is already done.
 
If you do not want any more functionality in the data classes, their definition is already done.
Line 108: Line 107:
 
The following method loads the data from the JSON structure <source enclose="none">JSON_TESTDATA</source>  in the o object and returns the current values ​​of the properties then on the console.
 
The following method loads the data from the JSON structure <source enclose="none">JSON_TESTDATA</source>  in the o object and returns the current values ​​of the properties then on the console.
  
<source>
+
<syntaxhighlight>
 
procedure DeStreamTest;
 
procedure DeStreamTest;
 
var
 
var
Line 142: Line 141:
 
   end;
 
   end;
 
end;
 
end;
</source>
+
</syntaxhighlight>
  
 
== JSON store ==
 
== JSON store ==
  
To convert an object into a JSON text, the class [[TJSONStreamer]] is used. Here is the method <source enclose="none">Function ObjectToJSONString(AObject : TObject) : TJSONStringType;</source> used.
+
To convert an object into a JSON text, the class [[TJSONStreamer]] is used. Here is the method <syntaxhighlight enclose="none">Function ObjectToJSONString(AObject : TObject) : TJSONStringType;</syntaxhighlight> used.
  
 
In the following procedure, an object is created, filled with the test data, and then transferred to JSON. The JSON text is printed to the console. It can not be defined, the order in which the properties are to spend
 
In the following procedure, an object is created, filled with the test data, and then transferred to JSON. The JSON text is printed to the console. It can not be defined, the order in which the properties are to spend
  
<source>
+
<syntaxhighlight>
 
procedure StreamTest;
 
procedure StreamTest;
 
var
 
var
Line 182: Line 181:
 
   end;
 
   end;
 
end;
 
end;
</source>
+
</syntaxhighlight>
  
 
== Outlook ==
 
== Outlook ==
Line 194: Line 193:
 
* [[JSON]]
 
* [[JSON]]
 
* [[fcl-json]]
 
* [[fcl-json]]
* [[Streaming components/de]]
+
* [[Streaming components]]
  
 
== Notes & References ==
 
== Notes & References ==
Line 200: Line 199:
 
# <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>
  
[[Category:Tutorials/de]] [[Category:JSON]] [[Category:Deutsch]]
+
[[Category:Tutorials]]
 +
[[Category:JSON]]
 
[[Category:JavaScript]]
 
[[Category:JavaScript]]

Revision as of 09:32, 5 November 2014

Template:Translate

note: This article was originally translated from Streaming_JSON/de German on 20141002 using Google Translat.

JSON (JavaScript Object Notation) is a text-based, standardized data format. As the name implies, JSON documents are valid JavaScript code where they can be directly converted into JavaScript objects. But in and of itself it can be used regardless of the programming language used for data exchange.

This tutorial explains how to load JSON data in a Free Pascal program to process it. It also explains how to convert FPC data into JSON (e.g. to send it to a web server).

General and conditions

Loading and storing (streaming) FPC object data is done with the fpjsonrtti unit. Furthermore, the Classes unit is useful (see below for details).

The uses statement should therefore contain at least these two units:

uses Classes, fpjsonrtti;

Currently (May 2014) there are some differences to the streaming system of Free Pascal with ??presumably JavaScript??:

  • JSON data is case-sensitive. Therefore, the properties of Free Pascal objects as JSON properties must be written with correct casing.
  • No properties can be defined with DefineProperties. Neither can references to methods (event handlers) be stored. 2
  • TCollection and TStrings could be used as a substitute for arrays.

Demo programs are provided with the source code of Free Pascal Compiler (in the packages/fcl-json/examples directory).

As an example, we will start with the following JSON data:

{
  "id"     : 123,                                                // an Integer
  "obj"    : { "name": "Hello world!" },                          // an Object
  "coll"   : [ { "name": "Object 1" }, { "name": "Object 2" } ], // two objects in a TCollection 
  "strings": [ "Hallo 1", "Hello 2" ]                            // a string list
}

This can be assigned in FreePascal using a constant assignment as follows:

const JSON_TESTDATA =
'{'+LineEnding+
'  "id": 123,'+LineEnding+
'  "obj": { "name": "Hello world!" },'+LineEnding+
'  "coll": [ { "name": "Object 1" }, { "name": "Object 2" } ],'+LineEnding+
'  "strings": [ "Hello 1", "Hello 2" ]'+LineEnding+
'}';

Data Structure

As the base class for the data itself provides TPersistent on from the Classes unit, since for them and all subclasses Laufzeit-Typinformationen (RTTI) to be created. These are absolutely required for streaming. Since fpjsonrtti not integrated into the streaming system, any other class that the compiler switch {$M+} was translated, are used.

All properties must be read as a property in published section of the class are declared. In general, (the variable) may here with read and write directly to a data field will refer. If you want to, of course getter and setter methods can be used.

From the JSON structure results in the following class definition.

type
  TNameObject = class(TCollectionItem) // class for the property 'obj' and the TCollection 
  private
    fName: String;
  published
    property name: String read fName write fName;
  end;  

  TBaseObject = class(TPersistent)  // class for the JSON structure
  private
    fid: Integer;
    fObj: TNameObject;
    fColl: TCollection;
    fStrings: TStrings;
  public
    constructor Create;
    destructor Destroy; override;
  published  // all properties have published 
    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;

The class TNameObject is of TCollectionItem derived. So they can both for the property obj be used as well in the collection. If this is not desired, here two different classes must be defined.

In the constructor of the class TBaseObject the TCollection and the string list must be created and released in the destructor.

constructor TBaseObject.Create;
begin
  // Collection and StringList constructor
  fColl    := TCollection.Create(TNameObject);
  fStrings := TStringList.Create;
  fObj     := TNameObject.Create(nil);
end;

destructor TBaseObject.Destroy;
begin
  // Collection and StringList destructor
  fColl.Free;
  fStrings.Free;
  fObj.Free;
  inherited Destroy;
end;

If you do not want any more functionality in the data classes, their definition is already done.

Load JSON

With the method Procedure JSONToObject(Const JSON : TJSONStringType; AObject : TObject); the class TJSONDeStreamer you can JSON data directly to an existing assigned object. Before you call the method, you must create TJSONDeStreamer and the target object.

The following method loads the data from the JSON structure JSON_TESTDATA in the o object and returns the current values ​​of the properties then on the console.

procedure DeStreamTest;
var
  DeStreamer: TJSONDeStreamer;
  o: TBaseObject;
  no: TNameObject;
  s: String;
begin
  WriteLn('DeStream test');
  WriteLn('======================================');

  // DeStreamer object and target object create 
  DeStreamer := TJSONDeStreamer.Create(nil);
  o := TBaseObject.Create;
  try
    // JSON data in the object o Load 
    DeStreamer.JSONToObject(JSON_TESTDATA, o);
    // ID
    WriteLn(o.id);
    // Object-Name
    WriteLn(o.obj.name); 
    // Print the names of all objects
    for TCollectionItem(no) in o.coll do
      Writeln(no.name);
    // all strings output 
    for s in o.strings do
      WriteLn(s);

  // Cleanup
  finally
    o.Destroy;
    DeStreamer.Destroy;
  end;
end;

JSON store

To convert an object into a JSON text, the class TJSONStreamer is used. Here is the method Function ObjectToJSONString(AObject : TObject) : TJSONStringType; used.

In the following procedure, an object is created, filled with the test data, and then transferred to JSON. The JSON text is printed to the console. It can not be defined, the order in which the properties are to spend

procedure StreamTest;
var
  Streamer: TJSONStreamer;
  o: TBaseObject;
  JSONString: String;
begin
  WriteLn('Stream test');
  WriteLn('======================================');

  Streamer := TJSONStreamer.Create(nil);
  o := TBaseObject.Create;
  try
    // Data set
    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
    // JSON convert and output
    JSONString := Streamer.ObjectToJSONString(o);
    WriteLn(JSONString);

  // Cleanup
  finally
    o.Destroy;
    Streamer.Destroy;
  end;
end;

Outlook

With the presented knowledge simple and complex JSON data structures in Free Pascal can be loaded. Should any preparatory or finishing the JSON data be necessary, the text data can initially with the classTJSONParser from the Unit jsonparser be loaded into a JSON data structure and then with the Unit fpJSON be manipulated.

Use the Options property of the class TJSONStreamer can be controlled, such as the issue's own data structures are represented in JSON.


See Also

Notes & References