Difference between revisions of "Streaming JSON"

From Free Pascal wiki
Jump to navigationJump to search
(Please don't ever use Google translate to translate articles without post editing. It is incomprehensible.)
(Please don't ever use Google translate to translate articles without post editing. It is incomprehensible.)
Line 1: Line 1:
 
{{Translate}}
 
{{Translate}}
  
note: This article was originally translated from [[Streaming_JSON/de]] German on 20141002 using Google Translat.
+
{{Note|This article was originally translated from [[Streaming_JSON/de]] German on 20141002 using Google Translate. However, the results were incomprehensible English and incorrect data so extensive manual editing was necessary. '''Please do not just use automatic translation tools without correcting the output!'''}}
  
 
[[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.
 
[[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.

Revision as of 10:12, 7 November 2014

Template:Translate

Light bulb  Note: This article was originally translated from Streaming_JSON/de German on 20141002 using Google Translate. However, the results were incomprehensible English and incorrect data so extensive manual editing was necessary. Please do not just use automatic translation tools without correcting the output!

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;

Saving JSON

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

In the following procedure, an object is created, filled with the test data, and then converted to JSON. The order in which the properties are saved cannot be influenced by the programmer.

Finally, the JSON text is printed to the console.

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 := 'Hello world!';
    TNameObject(o.coll.Add).name := 'Object 1';
    TNameObject(o.coll.Add).name := 'Object 2';
    o.strings.Add('Hello 1');
    o.strings.Add('Hello 2');

    Streamer.Options := Streamer.Options + [jsoTStringsAsArray]; // Save strings as JSON array
    // JSON convert and output
    JSONString := Streamer.ObjectToJSONString(o);
    WriteLn(JSONString);

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

Conclusion

With the presented knowledge, simple and complex JSON data structures can be loaded in your Free Pascal programs. Should any pre- or postprocessing of he JSON data be necessary, the text data can be initially loaded into a JSON data structure (with the classTJSONParser from the Unit jsonparser) and then manipulated with the Unit fpJSON.

The TJSONStreamer's Options property can be used to control the representation of your own data structures in JSON.

See Also

Notes & References