Sign in Join
Wiki copytemporary

Streaming JSON/zh CN: what changed

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

Title: Streaming JSON/zh CN

OldNewLine
1+[[JSON]] (JavaScript Object Notation)是一种基于文本的标准化数据格式。顾名思义,JSON 文档是有效的 JavaScript 代码,可以直接转换为 JavaScript 对象。但是,无论使用哪种编程语言,JSON 都可用于数据交换。
2+
3+本教程解释了如何将 JSON 数据加载到免费的 Pascal 程序中并在那里进行处理。它还解释了如何将数据从程序转换为JSON(例如将其发送到Web浏览器)。
4+
5+## General requirements
6+
7+加载和存储(流式处理)对象是通过 fpjsonrtti 单元完成的。使用类单元也很有意义(有关详细信息,请参见下文)。
8+
9+因此,uses 语句应至少包含以下两个单元:
10+
11+``` pascal
12+uses Classes, fpjsonrtti;
13+```
14+
15+目前(2014 年 \<\> 月)Free Pascal 的流媒体系统和 JSON 之间存在一些差异:
16+
17+- JSON 是区分大小写的数据格式。因此,Free Pascal对象的属性必须以与JSON属性相同的方式编写。
18+- No properties can be defined with DefineProperties. No references to methods (event handlers) can be saved.2
19+- [[TCollection]] and [[TStrings]] can used as a replacements for arrays.
20+
21+Demo programs are provided with Free Pascal Compiler source code in the `packages/fcl-json/examples` directory.
22+
23+This JSON structure is used in the examples which follow:
24+
25+``` JavaScript
26+{
27+ "id" : 123, // an integer
28+ "obj" : { "name": "Hello world!" }, // an object
29+ "coll" : [ { "name": "Object 1" }, { "name": "Object 2" } ], // two objects in a TCollection
30+ "strings": [ "Hello 1", "Hello 2" ] // a string list
31+}
32+```
33+
34+It can be defined in your Free Pascal program using a constant assignment as follows:
35+
36+``` pascal
37+const JSON_TESTDATA =
38+'{'+LineEnding+
39+' "id": 123,'+LineEnding+
40+' "obj": { "name": "Hello world!" },'+LineEnding+
41+' "coll": [ { "name": "Object 1" }, { "name": "Object 2" } ],'+LineEnding+
42+' "strings": [ "Hello 1", "Hello 2" ]'+LineEnding+
43+'}';
44+```
45+
46+## Data Structure
47+
48+The 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.
49+
50+All 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.
51+
52+The following class definition results from the JSON structure:
53+
54+``` pascal
55+type
56+ TNameObject = class(TCollectionItem) // class for the 'obj' property and TCollection
57+ private
58+ fName: String;
59+ published
60+ property name: String read fName write fName;
61+ end;
62+
63+ TBaseObject = class(TPersistent) // class for the entire JSON structure
64+ private
65+ fid: Integer;
66+ fObj: TNameObject;
67+ fColl: TCollection;
68+ fStrings: TStrings;
69+ public
70+ constructor Create;
71+ destructor Destroy; override;
72+ published // all properties must be published
73+ property id: Integer read fid write fid;
74+ property obj: TNameObject read fObj write fObj;
75+ property coll: TCollection read fColl;
76+ property strings: TStrings read fStrings;
77+ end;
78+```
79+
80+The `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.
81+
82+The TCollection and the string list must be created in the constructor of the TBaseObject class and released in the destructor.
83+
84+``` pascal
85+constructor TBaseObject.Create;
86+begin
87+ // Create Collection and StringList
88+ fColl := TCollection.Create(TNameObject);
89+ fStrings := TStringList.Create;
90+ fObj := TNameObject.Create(nil);
91+end;
92+
93+destructor TBaseObject.Destroy;
94+begin
95+ // Release Collection and StringList
96+ fColl.Free;
97+ fStrings.Free;
98+ fObj.Free;
99+ inherited Destroy;
100+end;
101+```
102+
103+If you do not want any more functionality in the data classes, their definition is now complete.
104+
105+## Load JSON
106+
107+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.
108+
109+The 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.
110+
111+``` pascal
112+procedure DeStreamTest;
113+var
114+ DeStreamer: TJSONDeStreamer;
115+ o: TBaseObject;
116+ no: TNameObject;
117+ s: String;
118+begin
119+ WriteLn('DeStream test');
120+ WriteLn('======================================');
121+
122+ // Create the DeStreamer object and target object
123+ DeStreamer := TJSONDeStreamer.Create(nil);
124+ o := TBaseObject.Create;
125+ try
126+ // Load JSON data into object o
127+ DeStreamer.JSONToObject(JSON_TESTDATA, o);
128+ // output ID
129+ WriteLn(o.id);
130+ // output object name
131+ WriteLn(o.obj.name);
132+ // output the names of all objects
133+ for TCollectionItem(no) in o.coll do
134+ Writeln(no.name);
135+ // output all strings
136+ for s in o.strings do
137+ WriteLn(s);
138+
139+ // Cleanup
140+ finally
141+ o.Destroy;
142+ DeStreamer.Destroy;
143+ end;
144+end;
145+```
146+
147+## Saving JSON
148+
149+The class [[TJSONStreamer]] is used to convert an object into JSON text. Here the method `Function ObjectToJSONString(AObject : TObject) : TJSONStringType;` is used.
150+
151+In 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.
152+
153+``` pascal
154+procedure StreamTest;
155+var
156+ Streamer: TJSONStreamer;
157+ o: TBaseObject;
158+ JSONString: String;
159+begin
160+ WriteLn('Stream test');
161+ WriteLn('======================================');
162+
163+ Streamer := TJSONStreamer.Create(nil);
164+ o := TBaseObject.Create;
165+ try
166+ // Setup data
167+ o.id := 123;
168+ o.obj.name := 'Hello world!';
169+ TNameObject(o.coll.Add).name := 'Object 1';
170+ TNameObject(o.coll.Add).name := 'Object 2';
171+ o.strings.Add('Hello 1');
172+ o.strings.Add('Hello 2');
173+
174+ Streamer.Options := Streamer.Options + [jsoTStringsAsArray]; // Save strings as JSON array
175+ // convert to JSON and output to console
176+ JSONString := Streamer.ObjectToJSONString(o);
177+ WriteLn(JSONString);
178+
179+ // Cleanup
180+ finally
181+ o.Destroy;
182+ Streamer.Destroy;
183+ end;
184+end;
185+```
186+
187+## Custom properties
188+
189+Now 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.
190+
191+This 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.
192+
193+This customizes how TColor is saved and loaded.
194+
195+Saving code:
196+
197+``` pascal
198+procedure TForm1.Button1Click(Sender: TObject);
199+var
200+ Streamer: TJSONStreamer;
201+ JSONString: String;
202+begin
203+ Streamer := TJSONStreamer.Create(nil);
204+ try
205+ Streamer.OnStreamProperty:=@OnStreamProperty;
206+ JSONString := Streamer.ObjectToJSONString(BCButton1.StateNormal);
207+ Memo1.Lines.Text := JSONString;
208+ finally
209+ Streamer.Destroy;
210+ end;
211+end;
212+```
213+
214+``` pascal
215+procedure TForm1.OnStreamProperty(Sender: TObject; AObject: TObject;
216+ Info: PPropInfo; var Res: TJSONData);
217+var
218+ bgracolor: TBGRAPixel;
219+begin
220+ if (Info^.PropType^.Name = 'TGraphicsColor') then
221+ begin
222+ bgracolor := ColorToBGRA(TColor(GetPropValue(AObject, Info, False)));
223+ Res.Free;
224+ Res := TJSONString.Create('rgb('+IntToStr(bgracolor.red)+','+IntToStr(bgracolor.green)+','+IntToStr(bgracolor.blue)+')');
225+ end;
226+end;
227+```
228+
229+Loading code:
230+
231+``` pascal
232+procedure TForm1.Button2Click(Sender: TObject);
233+var
234+ DeStreamer: TJSONDeStreamer;
235+ s: String;
236+begin
237+ DeStreamer := TJSONDeStreamer.Create(nil);
238+ try
239+ DeStreamer.OnRestoreProperty:=@OnRestoreProperty;
240+ DeStreamer.JSONToObject(Memo1.Lines.Text, BCButton1.StateNormal);
241+ finally
242+ DeStreamer.Destroy;
243+ end;
244+end;
245+```
246+
247+``` pascal
248+procedure TForm1.OnRestoreProperty(Sender: TObject; AObject: TObject;
249+ Info: PPropInfo; AValue: TJSONData; var Handled: Boolean);
250+var
251+ bgracolor: TBGRAPixel;
252+begin
253+ Handled := False;
254+ if (Info^.PropType^.Name = 'TGraphicsColor') then
255+ begin
256+ Handled := True;
257+ bgracolor := StrToBGRA(AValue.AsString);
258+ SetPropValue(AObject, Info, BGRAToColor(bgracolor));
259+ end;
260+end;
261+```
262+
263+## Conclusion
264+
265+With 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.
266+
267+The Options property of the TJSONStreamer class can be used to control how the output maps its own data structures in JSON.
268+
269+## See Also
270+
271+- [[JSON]]
272+- [[fcl-json]]
273+- [[Streaming components]]
274+
275+## Notes & References
276+
277+1. <div id="ref1">
278+
279+ <http://lists.freepascal.org/fpc-pascal/2013-January/036254.html>
280+
281+ </div>
282+
283+2. \<div id="ref2\>http://lists.lazarus.freepascal.org/pipermail/lazarus/2011-January/058878.html
284+ </div>
285+
286+---
287+*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