Sign in Join
Wiki copytemporary

Streaming JSON/zh CN

Deutsch · English · Polski · Русский · 中文 · 中文(中国)

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

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

General requirements

加载和存储(流式处理)对象是通过 fpjsonrtti 单元完成的。使用类单元也很有意义(有关详细信息,请参见下文)。

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

pascal
uses Classes, fpjsonrtti;

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

示例程序与Free Pascal Compiler源代码一起提供在packages/fcl-json/examples目录中。

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

pascal
{
      "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
    }

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

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

数据的基础类是来自Classes单元的TPersistent,为其及所有子类创建了runtime type information (RTTI)。这些对于流处理是至关重要的。由于fpjsonrtti并未集成到流处理系统中,因此也可以使用编译器开关 {$M+} 翻译的任何其他类。

要读取的所有property 必须在类的 published 部分中声明为属性。通常,您可以直接使用read和write来引用数据字段(变量)。当然,如果您愿意,也可以使用getter和setter方法。

以下类定义由JSON结构得出:

pascal
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;

TNameObject类是从 TCollectionItem派生的。这意味着它既可以用于obj属性,也可以用于集合中。如果这不是所期望的,那么必须在此处定义两个不同的类。

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

pascal
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类中的方法Procedure JSONToObject(Const JSON : TJSONStringType; AObject : TObject);,您可以将JSON数据直接分配给“现有”对象。在调用此方法之前,您必须创建TJSONDeStreamer和目标对象。

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

pascal
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文本将输出到控制台。无法指定属性输出的顺序。

pascal
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;

既然我们能够以JSON格式进行保存和加载,我们也可以自定义属性的保存方式。默认情况下,TColor(TGraphicsColor)被保存为数字,但例如,我们希望将其保存为字符串,这样更便于人们阅读。

此示例使用了BGRABitmap库,该库具有一个可以轻松从字符串转换为颜色的函数。在这种情况下,流控制是来自BGRAControls包的按钮控件。

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

Saving code:

pascal
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;
pascal
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;

Loading code:

pascal
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;
pascal
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单元按需进行处理。

See Also

Notes & References

  1. http://lists.freepascal.org/fpc-pascal/2013-January/036254.html
  1. http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-January/058878.html

From the Free Pascal wiki, page "Streaming JSON/zh CN", as the Internet Archive kept it on 2025-02-17, last changed on 2024-09-14. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 3, last changed by WikiArchive .

Edited on wiki.freepascal.org by 1 person, 2023

From the official wiki's backup of 26 May 2024: who edited this page there, and when. Edits made there after that date were not recorded, and the earlier versions are not shown here; the main page says why.

WhenWhoWhat changed
ClzwfCreated page

Talk about this page

Nobody has opened a bench for this page yet. Sign in to open one.