Sign in Join
Wiki copytemporary

Streaming JSON/zh CN: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11[[JSON]] (JavaScript Object Notation)是一种基于文本的标准化数据格式。顾名思义,JSON 文档是有效的 JavaScript 代码,可以直接转换为 JavaScript 对象。但是,无论使用哪种编程语言,JSON 都可用于数据交换。
22
33本教程解释了如何将 JSON 数据加载到免费的 Pascal 程序中并在那里进行处理。它还解释了如何将数据从程序转换为JSON(例如将其发送到Web浏览器)。
44
55## General requirements
66
77加载和存储(流式处理)对象是通过 fpjsonrtti 单元完成的。使用类单元也很有意义(有关详细信息,请参见下文)。
88
99因此,uses 语句应至少包含以下两个单元:
1010
1111``` pascal
1212uses Classes, fpjsonrtti;
1313```
1414
1515目前(2014 年 \<\> 月)Free Pascal 的流媒体系统和 JSON 之间存在一些差异:
1616
1717- JSON 是区分大小写的数据格式。因此,Free Pascal对象的属性必须以与JSON属性相同的方式编写。
1818- No properties can be defined with DefineProperties. No references to methods (event handlers) can be saved.2
1919- [[TCollection]] and [[TStrings]] can used as a replacements for arrays.
2020
2121Demo programs are provided with Free Pascal Compiler source code in the `packages/fcl-json/examples` directory.
2222
2323This JSON structure is used in the examples which follow:
2424
2525``` JavaScript
2626{
2727 "id" : 123, // an integer
2828 "obj" : { "name": "Hello world!" }, // an object
2929 "coll" : [ { "name": "Object 1" }, { "name": "Object 2" } ], // two objects in a TCollection
3030 "strings": [ "Hello 1", "Hello 2" ] // a string list
3131}
3232```
3333
3434It can be defined in your Free Pascal program using a constant assignment as follows:
3535
3636``` pascal
3737const JSON_TESTDATA =
3838'{'+LineEnding+
3939' "id": 123,'+LineEnding+
4040' "obj": { "name": "Hello world!" },'+LineEnding+
4141' "coll": [ { "name": "Object 1" }, { "name": "Object 2" } ],'+LineEnding+
4242' "strings": [ "Hello 1", "Hello 2" ]'+LineEnding+
4343'}';
4444```
4545
4646## Data Structure
4747
4848The base class for the data is [[TPersistent]] from the Classes unit, [[Runtime Type Information (RTTI)|runtime type information (RTTI)]] is created for it and all subclasses. These are essential for streaming. Since fpjsonrtti does not integrate into the streaming system, any other class translated with the compiler switch **{\$M+}** can also be used.
4949
5050All properties to be read must be declared as a [[Property|property]] in the [[Published|published]] section of the class. As a rule, you can use read and write to refer directly to a data field (the variable). If you want, you can of course use getter and setter methods.
5151
5252The following class definition results from the JSON structure:
5353
5454``` pascal
5555type
5656 TNameObject = class(TCollectionItem) // class for the 'obj' property and TCollection
5757 private
5858 fName: String;
5959 published
6060 property name: String read fName write fName;
6161 end;
6262
6363 TBaseObject = class(TPersistent) // class for the entire JSON structure
6464 private
6565 fid: Integer;
6666 fObj: TNameObject;
6767 fColl: TCollection;
6868 fStrings: TStrings;
6969 public
7070 constructor Create;
7171 destructor Destroy; override;
7272 published // all properties must be published
7373 property id: Integer read fid write fid;
7474 property obj: TNameObject read fObj write fObj;
7575 property coll: TCollection read fColl;
7676 property strings: TStrings read fStrings;
7777 end;
7878```
7979
8080The `TNameObject` class was derived from [[TCollectionItem]]. This means that it can be used both for the obj property and in the collection. If this is not desired, then two different classes must be defined here.
8181
8282The TCollection and the string list must be created in the constructor of the TBaseObject class and released in the destructor.
8383
8484``` pascal
8585constructor TBaseObject.Create;
8686begin
8787 // Create Collection and StringList
8888 fColl := TCollection.Create(TNameObject);
8989 fStrings := TStringList.Create;
9090 fObj := TNameObject.Create(nil);
9191end;
9292
9393destructor TBaseObject.Destroy;
9494begin
9595 // Release Collection and StringList
9696 fColl.Free;
9797 fStrings.Free;
9898 fObj.Free;
9999 inherited Destroy;
100100end;
101101```
102102
103103If you do not want any more functionality in the data classes, their definition is now complete.
104104
105105## Load JSON
106106
107107With 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.
108108
109109The following method loads the data from the JSON structure `JSON_TESTDATA` in the object o and then outputs the current values ​​of the properties to the console.
110110
111111``` pascal
112112procedure DeStreamTest;
113113var
114114 DeStreamer: TJSONDeStreamer;
115115 o: TBaseObject;
116116 no: TNameObject;
117117 s: String;
118118begin
119119 WriteLn('DeStream test');
120120 WriteLn('======================================');
121121
122122 // Create the DeStreamer object and target object
123123 DeStreamer := TJSONDeStreamer.Create(nil);
124124 o := TBaseObject.Create;
125125 try
126126 // Load JSON data into object o
127127 DeStreamer.JSONToObject(JSON_TESTDATA, o);
128128 // output ID
129129 WriteLn(o.id);
130130 // output object name
131131 WriteLn(o.obj.name);
132132 // output the names of all objects
133133 for TCollectionItem(no) in o.coll do
134134 Writeln(no.name);
135135 // output all strings
136136 for s in o.strings do
137137 WriteLn(s);
138138
139139 // Cleanup
140140 finally
141141 o.Destroy;
142142 DeStreamer.Destroy;
143143 end;
144144end;
145145```
146146
147147## Saving JSON
148148
149149The class [[TJSONStreamer]] is used to convert an object into JSON text. Here the method `Function ObjectToJSONString(AObject : TObject) : TJSONStringType;` is used.
150150
151151In the following procedure, an object is created, filled with the test data, and then converted to JSON. The JSON text is output on the console. The order in which the properties are output cannot be specified.
152152
153153``` pascal
154154procedure StreamTest;
155155var
156156 Streamer: TJSONStreamer;
157157 o: TBaseObject;
158158 JSONString: String;
159159begin
160160 WriteLn('Stream test');
161161 WriteLn('======================================');
162162
163163 Streamer := TJSONStreamer.Create(nil);
164164 o := TBaseObject.Create;
165165 try
166166 // Setup data
167167 o.id := 123;
168168 o.obj.name := 'Hello world!';
169169 TNameObject(o.coll.Add).name := 'Object 1';
170170 TNameObject(o.coll.Add).name := 'Object 2';
171171 o.strings.Add('Hello 1');
172172 o.strings.Add('Hello 2');
173173
174174 Streamer.Options := Streamer.Options + [jsoTStringsAsArray]; // Save strings as JSON array
175175 // convert to JSON and output to console
176176 JSONString := Streamer.ObjectToJSONString(o);
177177 WriteLn(JSONString);
178178
179179 // Cleanup
180180 finally
181181 o.Destroy;
182182 Streamer.Destroy;
183183 end;
184184end;
185185```
186186
187187## Custom properties
188188
189189Now that we can save and load to JSON, we can as well customize how the property is saved. By default a TColor (TGraphicsColor) is saved as a number, but for example we want to make it a string, so is more human readable.
190190
191191This example uses BGRABitmap library, that has a function to convert from string to color easily. The streamed control in this case is a button control from BGRAControls package.
192192
193193This customizes how TColor is saved and loaded.
194194
195195Saving code:
196196
197197``` pascal
198198procedure TForm1.Button1Click(Sender: TObject);
199199var
200200 Streamer: TJSONStreamer;
201201 JSONString: String;
202202begin
203203 Streamer := TJSONStreamer.Create(nil);
204204 try
205205 Streamer.OnStreamProperty:=@OnStreamProperty;
206206 JSONString := Streamer.ObjectToJSONString(BCButton1.StateNormal);
207207 Memo1.Lines.Text := JSONString;
208208 finally
209209 Streamer.Destroy;
210210 end;
211211end;
212212```
213213
214214``` pascal
215215procedure TForm1.OnStreamProperty(Sender: TObject; AObject: TObject;
216216 Info: PPropInfo; var Res: TJSONData);
217217var
218218 bgracolor: TBGRAPixel;
219219begin
220220 if (Info^.PropType^.Name = 'TGraphicsColor') then
221221 begin
222222 bgracolor := ColorToBGRA(TColor(GetPropValue(AObject, Info, False)));
223223 Res.Free;
224224 Res := TJSONString.Create('rgb('+IntToStr(bgracolor.red)+','+IntToStr(bgracolor.green)+','+IntToStr(bgracolor.blue)+')');
225225 end;
226226end;
227227```
228228
229229Loading code:
230230
231231``` pascal
232232procedure TForm1.Button2Click(Sender: TObject);
233233var
234234 DeStreamer: TJSONDeStreamer;
235235 s: String;
236236begin
237237 DeStreamer := TJSONDeStreamer.Create(nil);
238238 try
239239 DeStreamer.OnRestoreProperty:=@OnRestoreProperty;
240240 DeStreamer.JSONToObject(Memo1.Lines.Text, BCButton1.StateNormal);
241241 finally
242242 DeStreamer.Destroy;
243243 end;
244244end;
245245```
246246
247247``` pascal
248248procedure TForm1.OnRestoreProperty(Sender: TObject; AObject: TObject;
249249 Info: PPropInfo; AValue: TJSONData; var Handled: Boolean);
250250var
251251 bgracolor: TBGRAPixel;
252252begin
253253 Handled := False;
254254 if (Info^.PropType^.Name = 'TGraphicsColor') then
255255 begin
256256 Handled := True;
257257 bgracolor := StrToBGRA(AValue.AsString);
258258 SetPropValue(AObject, Info, BGRAToColor(bgracolor));
259259 end;
260260end;
261261```
262262
263263## Conclusion
264264
265265With the knowledge presented, simple and complex JSON data structures can be loaded into Free Pascal programs. Should any pre- or post-processing of the JSON data be necessary, the text data can be first loaded from the [[jsonparser]] unit into a JSON data structure using the [[TJSONParser]] class and then manipulated as desired with the [[fpJSON]] unit.
266266
267267The Options property of the TJSONStreamer class can be used to control how the output maps its own data structures in JSON.
268268
269269## See Also
270270
271271- [[JSON]]
272272- [[fcl-json]]
273273- [[Streaming components]]
274274
275275## Notes & References
276276
277-1. <div id="ref1">
278-
279- <http://lists.freepascal.org/fpc-pascal/2013-January/036254.html>
280-
281- </div>
277+1. <http://lists.freepascal.org/fpc-pascal/2013-January/036254.html>
282278
2832792. \<div id="ref2\>http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-January/058878.html
284- </div>
285280
286281---
287282*From the Free Pascal wiki, page "Streaming JSON/zh CN", as it stood on 2023-05-01 after a change by Clzwf, out of the wiki's dump of 2024-05-26. 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](/wiki-contact).*

Back to history