Sign in Join
Wiki copytemporary

cnocstream: what changed

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

OldNewLine
11# cnocStream
22
33This package is a library to serialize objects to streamable contents. For example: stream an object into a JSON-format, or xml. And vice-versa. It is work in progress, and I'm seeking help. The goal is to make a library which will make all others obsolete. This mean that a good design is crucial. So if you can have had a look and have some ideas how to improve it, mail me: \<joost@cnoc.nl\>. (Ideas for another name are also welcome). The idea has also been discussed on the \[fpc-pascal mailinglist\](https://lists.freepascal.org/pipermail/fpc-pascal/2019-August/056486.html).
44
55At this moment (aug 2019) it is all just work-in-progress. It might be that nothing works, or that everything will change in the future.
66
77# How to use
88
99## Using one of the helpers
1010
1111There are several helpers to stream into several formats using several techniques. (Well, in theory, now there is only JSON and RTTI) For example, use the following to serialize a instance of TComponent into JSON format:
1212
13-`   uses`\
14-`     Classes,`\
15-`     csJSONRttiStreamHelper;`\
16-`   var`\
17-`     Comp: TComponent;`\
18-`   begin`\
19-`     Comp := TComponent.Create(nil);`\
20-`     Comp.Name := 'MyComponent';`\
21-`     writeln(TJSONRttiStreamHelper.ObjectToJSONString(Comp));`\
22-`     Comp.Free;`\
23-`   end.`
13+``` pascal
14+ uses
15+ Classes,
16+ csJSONRttiStreamHelper;
17+ var
18+ Comp: TComponent;
19+ begin
20+ Comp := TComponent.Create(nil);
21+ Comp.Name := 'MyComponent';
22+ writeln(TJSONRttiStreamHelper.ObjectToJSONString(Comp));
23+ Comp.Free;
24+ end.
25+```
2426
2527This will print:
2628
27-`   { "Name" : "MyComponent", "Tag" : 0 }`
29+```text
30+ { "Name" : "MyComponent", "Tag" : 0 }
31+```
2832
2933The other way around looks like this:
3034
31-`   uses`\
32-`     Classes,`\
33-`     csJSONRttiStreamHelper;`\
34-`   var`\
35-`     Comp: TComponent;`\
36-`   begin`\
37-`     Comp := TComponent.Create(nil);`\
38-`     TJSONRttiStreamHelper.JSONStringToObject('{"Name":"MyObject"}', Comp);`\
39-`     writeln(Comp.Name); // Will return 'MyObject'`\
40-`     Comp.Free;`\
41-`   end.`
35+``` pascal
36+ uses
37+ Classes,
38+ csJSONRttiStreamHelper;
39+ var
40+ Comp: TComponent;
41+ begin
42+ Comp := TComponent.Create(nil);
43+ TJSONRttiStreamHelper.JSONStringToObject('{"Name":"MyObject"}', Comp);
44+ writeln(Comp.Name); // Will return 'MyObject'
45+ Comp.Free;
46+ end.
47+```
4248
4349## Using a description
4450
4551But what if you want to have some influence on how the class is streamed? For this there are descriptions (TcsStreamDescription). While it is possible to construct a description by yourself. It is easier to start with a description made by a describer. To obtain a description of TComponent based on the RTTI information, use the following:
4652
47-`   uses`\
48-`     classes,`\
49-`     csModel,`\
50-`     csRttiModel,`\
51-`     csJSONRttiStreamHelper;`\
52-`   function GetTComponentDescription: TcsStreamDescription;`\
53-`   var`\
54-`     Describer: TcsRttiDescriber;`\
55-`   begin`\
56-`     Describer := TcsRttiDescriber.Create;`\
57-`     Result :=Describer.ObtainDescriptionForClass(TComponent);`\
58-`     Describer.Free;`\
59-`   end;`
53+``` pascal
54+ uses
55+ classes,
56+ csModel,
57+ csRttiModel,
58+ csJSONRttiStreamHelper;
59+ function GetTComponentDescription: TcsStreamDescription;
60+ var
61+ Describer: TcsRttiDescriber;
62+ begin
63+ Describer := TcsRttiDescriber.Create;
64+ Result :=Describer.ObtainDescriptionForClass(TComponent);
65+ Describer.Free;
66+ end;
67+```
6068
61-`   var`\
62-`     Comp: TComponent;`\
63-`     Description: TcsStreamDescription;`\
64-`   begin`\
65-`     Comp := TComponent.Create(nil);`\
66-`     Comp.Name := 'MyComponent';`\
67-`     Description := GetTComponentDescription();`\
68-`     writeln(TJSONRttiStreamHelper.ObjectToJSONString(Comp));`\
69-`     Description.Free;`\
70-`     Comp.Free;`\
71-`   end.`
69+``` pascal
70+ var
71+ Comp: TComponent;
72+ Description: TcsStreamDescription;
73+ begin
74+ Comp := TComponent.Create(nil);
75+ Comp.Name := 'MyComponent';
76+ Description := GetTComponentDescription();
77+ writeln(TJSONRttiStreamHelper.ObjectToJSONString(Comp));
78+ Description.Free;
79+ Comp.Free;
80+ end.
81+```
7282
7383(Note that I left out proper exception/memory handling. This only confuscates the examples.)
7484
7585The description is passed as parameter to ObjectToJSONString. Note that this example prints the same result as before. But now it is possible to alter the given description. We could, for example, emit the 'tag' from the result:
7686
77-`   function GetTComponentDescription: TcsStreamDescription;`\
78-`   var`\
79-`     Describer: TcsRttiDescriber;`\
80-`     TagProp: TcsProperty;`\
81-`   begin`\
82-`     Describer := TcsRttiDescriber.Create;`\
83-`     Result :=Describer.ObtainDescriptionForClass(TComponent);`
87+``` pascal
88+ function GetTComponentDescription: TcsStreamDescription;
89+ var
90+ Describer: TcsRttiDescriber;
91+ TagProp: TcsProperty;
92+ begin
93+ Describer := TcsRttiDescriber.Create;
94+ Result :=Describer.ObtainDescriptionForClass(TComponent);
95+```
8496
85-`     // Adapt the description by removing the tag-property:`\
86-`     TagProp := Result.Properties.FindPropertyByName('Tag');`\
87-`     Result.Properties.Remove(TagProp);`
97+``` pascal
98+ // Adapt the description by removing the tag-property:
99+ TagProp := Result.Properties.FindPropertyByName('Tag');
100+ Result.Properties.Remove(TagProp);
101+```
88102
89-`     Describer.Free;`\
90-`   end;`
103+``` pascal
104+ Describer.Free;
105+ end;
106+```
91107
92108With this version of GetTComponentDescription the results looks like:
93109
94-`   { "Name" : "MyComponent" }`
110+```text
111+ { "Name" : "MyComponent" }
112+```
95113
96114## Re-use descriptions
97115
98116It could be quite tedious to create descriptions over and over again. One option to tacke this problem is by using a TcsStreamClass-descendant. They exist for several combinations of streaming-techniques and formats. They use a description-store to store the used descriptions. And it is possible to differenitate between different descriptions for one class by adding a description-tag. Take for example the following program:
99117
100-`   uses`\
101-`     classes,`\
102-`     csModel,`\
103-`     csJSONRttiStreamHelper;`\
104-`   var`\
105-`     Comp: TComponent;`\
106-`     Serializer: TJSONRttiStreamClass;`\
107-`     TagProp: TcsProperty;`\
108-`     Description: TcsStreamDescription;`\
109-`   begin`\
110-`     Serializer := TJSONRttiStreamClass.Create;`\
111-`     try`\
112-`       Description := Serializer.DescriptionStore.GetDescription(TComponent, 'without_tag');`\
113-`       TagProp := Description.Properties.FindPropertyByName('Tag');`\
114-`       Description.Properties.Remove(TagProp);`
118+``` pascal
119+ uses
120+ classes,
121+ csModel,
122+ csJSONRttiStreamHelper;
123+ var
124+ Comp: TComponent;
125+ Serializer: TJSONRttiStreamClass;
126+ TagProp: TcsProperty;
127+ Description: TcsStreamDescription;
128+ begin
129+ Serializer := TJSONRttiStreamClass.Create;
130+ try
131+ Description := Serializer.DescriptionStore.GetDescription(TComponent, 'without_tag');
132+ TagProp := Description.Properties.FindPropertyByName('Tag');
133+ Description.Properties.Remove(TagProp);
134+```
115135
116-`       Comp := TComponent.Create(nil);`\
117-`       Comp.Name := 'MyComponent';`\
118-`       writeln('With tag: ', Serializer.ObjectToJSONString(Comp));`\
119-`       writeln('Without tag: ', Serializer.ObjectToJSONString(Comp, 'without_tag'));`\
120-`       Comp.Free;`\
121-`     finally`\
122-`       Serializer.Free;`\
123-`     end;`\
124-`   end.`
136+``` pascal
137+ Comp := TComponent.Create(nil);
138+ Comp.Name := 'MyComponent';
139+ writeln('With tag: ', Serializer.ObjectToJSONString(Comp));
140+ writeln('Without tag: ', Serializer.ObjectToJSONString(Comp, 'without_tag'));
141+ Comp.Free;
142+ finally
143+ Serializer.Free;
144+ end;
145+ end.
146+```
125147
126148In the description-store of the (de)serializer there are two different description stored. One that does the serialization with and one without the tag. By calling ObjectToJSONString with the description-tag, it is possible to choose the used format (description).
127149
128150## Modifying the behaviour using descriptions
129151
130152There are more ways to modify the behaviour of the serialization then omitting properties. Descriptions can have several settings. When one such setting is not set, the setting of a parent description is used.
131153
132154This way, for example, it is possible to change the case of the property-names of all properties to lowercase, except for one property. This is done as follows:
133155
134-` {$M+}`\
135-` uses`\
136-`   csModel,`\
137-`   csJSONRttiStreamHelper;`\
138-` type`\
139-`   TMyType = class`\
140-`   private`\
141-`     FName: string;`\
142-`     FDescription: string;`\
143-`     FCamelCase: Integer;`\
144-`   published`\
145-`     property Name: string read FName write FName;`\
146-`     property Description: string read FDescription write FDescription;`\
147-`     property CamelCase: Integer read FCamelCase write FCamelCase;`\
148-`   end;`\
149-` var`\
150-`   Inst: TMyType;`\
151-`   Serializer: TJSONRttiStreamClass;`\
152-`   Description: TcsStreamDescription;`\
153-` begin`\
154-`   Serializer := TJSONRttiStreamClass.Create;`\
155-`   try`\
156-`     Description := Serializer.DescriptionStore.GetDescription(TMyType);`\
157-`     // Set the default behaviour to convert all property-names to lowercase`\
158-`     Description.ExportNameStyle := tcsensLowerCase;`\
159-`     // Make an exception for the property named CamelCase, this property`\
160-`     // should only have the first character converted to lowercase.`\
161-`     Description.Properties.FindPropertyByName('CamelCase').Describer.ExportNameStyle := tcsensLowerCaseFirstChar;`\
162-`     Inst := TMyType.Create;`\
163-`     Inst.Name := 'My instance';`\
164-`     Inst.Description := 'The description';`\
165-`     Inst.CamelCase := 101010;`\
166-`     writeln('Case test: ', Serializer.ObjectToJSONString(Inst));`\
167-`     Inst.Free;`\
168-`   finally`\
169-`     Serializer.Free;`\
170-`   end;`\
171-` end.`
156+``` pascal
157+ {$M+}
158+ uses
159+ csModel,
160+ csJSONRttiStreamHelper;
161+ type
162+ TMyType = class
163+ private
164+ FName: string;
165+ FDescription: string;
166+ FCamelCase: Integer;
167+ published
168+ property Name: string read FName write FName;
169+ property Description: string read FDescription write FDescription;
170+ property CamelCase: Integer read FCamelCase write FCamelCase;
171+ end;
172+ var
173+ Inst: TMyType;
174+ Serializer: TJSONRttiStreamClass;
175+ Description: TcsStreamDescription;
176+ begin
177+ Serializer := TJSONRttiStreamClass.Create;
178+ try
179+ Description := Serializer.DescriptionStore.GetDescription(TMyType);
180+ // Set the default behaviour to convert all property-names to lowercase
181+ Description.ExportNameStyle := tcsensLowerCase;
182+ // Make an exception for the property named CamelCase, this property
183+ // should only have the first character converted to lowercase.
184+ Description.Properties.FindPropertyByName('CamelCase').Describer.ExportNameStyle := tcsensLowerCaseFirstChar;
185+ Inst := TMyType.Create;
186+ Inst.Name := 'My instance';
187+ Inst.Description := 'The description';
188+ Inst.CamelCase := 101010;
189+ writeln('Case test: ', Serializer.ObjectToJSONString(Inst));
190+ Inst.Free;
191+ finally
192+ Serializer.Free;
193+ end;
194+ end.
195+```
172196
173197# Where to go from here
174198
175199Please have a look at the unit-tests in the tests directory. These tests could also be used as examples on how to use the library. Just use the sources if you want to know more about how it works internally.
176200
177201# Where to find the latest version?
178202
179203Good question. I'm still looking for a good place to host everything. Eventually it will be available in the fppkg-repository, I'll guess. For now I've uploaded it to [a temporary location](http://fpc.cnoc.nl/repo/cnocstream-0.1.source.zip)
180204
181205# License
182206
183207Before I forget: LGPL v2, with the linking-exception. Just like FPC uses for it's packages.
184208
185209Have fun,
186210
187211Joost van der Sluis / \<joost@cnoc.nl\>
188212
189213---
190214*From the Free Pascal wiki, page "cnocstream", as it stood on 2019-09-08 after a change by Loesje, 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