Streaming JSON/zh

From Free Pascal wiki
Jump to navigationJump to search

JSON(JavaScript Object Notation)是一种基于文本的标准化数据格式。顾名思义,JSON 文档是有效的 JavaScript 代码,可以直接转换为 JavaScript 对象。但是,无论使用哪种编程语言,JSON 都可用于数据交换。

本教程解释了如何将 JSON 数据加载到免费的 Pascal 程序中并在那里进行处理。它还解释了如何将数据从程序转换为JSON(例如将其发送到Web浏览器)。


目录 1 一般要求 2 数据结构 3 加载数据别名记录 4 保存 JSON 5 自定义属性 6 结论 7 参见 8 注释和参考文献 General requirements 加载和存储(流式处理)对象是通过 fpjsonrtti 单元完成的。使用类单元也很有意义(有关详细信息,请参见下文)。

因此,uses 语句应至少包含以下两个单元:

uses Classes, fpjsonrtti; 目前(2014 年 <> 月)Free Pascal 的流媒体系统和 JSON 之间存在一些差异:

JSON 是一种区分大小写的数据格式。因此,Free Pascal 对象的属性必须以与 JSON 属性相同的大小写方式编写。 不能使用 DefineProperties 定义任何属性。不能保存对方法(事件处理程序)的引用。阿拉伯数字 可以用作阵列的替代品。 演示程序在 packages/fcl-json/examples 目录中提供了免费的 Pascal 编译器源代码。

以下示例中使用此 JSON 结构:

{

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

} 它可以在您的自由 Pascal 程序中使用常量赋值定义,如下所示:

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 数据的基类是类单元中的 TPersistient,为它和所有子类创建运行时类型信息 (RTTI)。这些对于流式传输至关重要。由于 fpjsonrtti 不集成到流系统中,因此也可以使用使用编译器开关 {$M+} 翻译的任何其他类。

所有要读取的属性都必须在类的已发布部分中声明为属性。通常,您可以使用读取和写入直接引用数据字段(变量)。如果你愿意,你当然可以使用getter和setter方法。

以下类定义来自 JSON 结构:

type

 TNameObject = class(TCollectionItem) // class for the 'obj' property and TCollection 
 private
   fName: String;
 published
   property name: String read fName write fName;
 end;  
 TBaseObject = class(TPersistent)  // class for the entire JSON structure
 private
   fid: Integer;
   fObj: TNameObject;
   fColl: TCollection;
   fStrings: TStrings;
 public
   constructor Create;
   destructor Destroy; override;
 published                         // all properties must be 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;

该类派生自 TCollectionItem。这意味着它既可以用于 obj 属性,也可以用于集合。如果不需要这样做,则必须在此处定义两个不同的类。TNameObject

必须在 TBaseObject 类的构造函数中创建字符串列表,并在析构函数中释放。

constructor TBaseObject.Create; begin

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

end;

destructor TBaseObject.Destroy; begin

 // Release Collection and StringList
 fColl.Free;
 fStrings.Free;
 fObj.Free;
 inherited Destroy;

end; 如果您不希望数据类中有更多功能,则它们的定义现在已完成。

Load JSON 使用 TJSONDeStreamer 类中的方法,您可以将 JSON 数据直接分配给现有对象。在调用该方法之前,必须创建 TJSONDeStreamer 和目标对象。Procedure JSONToObject(Const JSON : TJSONStringType; AObject : TObject);

以下方法从对象 o 中的 JSON 结构加载数据,然后将属性的当前值输出到控制台。JSON_TESTDATA

procedure DeStreamTest; var

 DeStreamer: TJSONDeStreamer;
 o: TBaseObject;
 no: TNameObject;
 s: String;

begin

 WriteLn('DeStream test');
 WriteLn('======================================');
 // Create the DeStreamer object and target object
 DeStreamer := TJSONDeStreamer.Create(nil);
 o := TBaseObject.Create;
 try
   // Load JSON data into object o
   DeStreamer.JSONToObject(JSON_TESTDATA, o);
   // output ID
   WriteLn(o.id);
   // output object name
   WriteLn(o.obj.name); 
   // output 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 类TJSONStreamer用于将对象转换为JSON文本。这里使用该方法。Function ObjectToJSONString(AObject : TObject) : TJSONStringType;

在下面的过程中,将创建一个对象,用测试数据填充,然后将其转换为 JSON。JSON 文本在控制台上输出。无法指定属性的输出顺序。

procedure StreamTest; var

 Streamer: TJSONStreamer;
 o: TBaseObject;
 JSONString: String;

begin

 WriteLn('Stream test');
 WriteLn('======================================');
 Streamer := TJSONStreamer.Create(nil);
 o := TBaseObject.Create;
 try
   // Setup data
   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
   // convert to JSON and output to console
   JSONString := Streamer.ObjectToJSONString(o);
   WriteLn(JSONString);
 // Cleanup
 finally
   o.Destroy;
   Streamer.Destroy;
 end;

end; Custom properties 现在我们可以保存并加载到 JSON 中,我们还可以自定义属性的保存方式。默认情况下,TColor (TGraphicsColor) 被保存为数字,但例如我们希望将其设置为字符串,因此更易于人类阅读。

此示例使用 BGRABitmap 库,该库具有从字符串轻松转换为颜色的功能。在这种情况式处理的控件是 BGRAControls 包中的按钮控件。

这将自定义保存和加载 TColor 的方式。

保存代码:

procedure TForm1.Button1Click(Sender: TObject); var

 Streamer: TJSONStreamer;
 JSONString: String;

begin

 Streamer := TJSONStreamer.Create(nil);
 try
   Streamer.OnStreamProperty:=@OnStreamProperty;
   JSONString := Streamer.ObjectToJSONString(BCButton1.StateNormal);
   Memo1.Lines.Text := JSONString;
 finally
   Streamer.Destroy;
 end;

end; procedure TForm1.OnStreamProperty(Sender: TObject; AObject: TObject;

 Info: PPropInfo; var Res: TJSONData);

var

 bgracolor: TBGRAPixel;

begin

 if (Info^.PropType^.Name = 'TGraphicsColor') then
 begin
   bgracolor := ColorToBGRA(TColor(GetPropValue(AObject, Info, False)));
   Res.Free;
   Res := TJSONString.Create('rgb('+IntToStr(bgracolor.red)+','+IntToStr(bgracolor.green)+','+IntToStr(bgracolor.blue)+')');
 end;

end; 加载代码:

procedure TForm1.Button2Click(Sender: TObject); var

 DeStreamer: TJSONDeStreamer;
 s: String;

begin

 DeStreamer := TJSONDeStreamer.Create(nil);
 try
   DeStreamer.OnRestoreProperty:=@OnRestoreProperty;
   DeStreamer.JSONToObject(Memo1.Lines.Text, BCButton1.StateNormal);
 finally
   DeStreamer.Destroy;
 end;

end; procedure TForm1.OnRestoreProperty(Sender: TObject; AObject: TObject;

 Info: PPropInfo; AValue: TJSONData; var Handled: Boolean);

var

 bgracolor: TBGRAPixel;

begin

 Handled := False;
 if (Info^.PropType^.Name = 'TGraphicsColor') then
 begin
   Handled := True;
   bgracolor := StrToBGRA(AValue.AsString);
   SetPropValue(AObject, Info, BGRAToColor(bgracolor));
 end;

end; Conclusion 有了这些知识,简单和复杂的JSON数据结构可以加载到Free Pascal程序中。如果需要对 JSON 数据进行任何预处理或后处理,可以首先使用 TJSONParser 类将文本数据从 jsonparser 单元加载到 JSON 数据结构中,然后根据需要使用 fpJSON 单元进行操作。

类的 Options 属性可用于控制输出如何在 JSON 中映射其自己的数据结构。