Streaming JSON

From Lazarus wiki
Jump to navigationJump to search

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); in the TJSONDeStreamer class you can assign JSON data directly to an existing 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 then writes the current values ​​of the properties 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
    // Load JSON data in the object o
    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);
    // output all strings
    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