Difference between revisions of "LMD NG-Packs"
(Created page with "{{Head-Overview}} Category:FAQ") |
m (→NG SerializerPack) |
||
(One intermediate revision by the same user not shown) | |||
Line 1: | Line 1: | ||
{{Head-Overview}} [[Category:FAQ]] | {{Head-Overview}} [[Category:FAQ]] | ||
+ | |||
+ | ==NG SerializerPack== | ||
+ | ===How to serialize collections=== | ||
+ | Serializing collections, including custom or standard collections (like TList) requires writing custom converters. For example, lets consider the folowing collection declaration: | ||
+ | |||
+ | <pre class="brush:delphi"> | ||
+ | type | ||
+ | TItem = class | ||
+ | public | ||
+ | X: Integer; | ||
+ | Y: Integer; | ||
+ | end; | ||
+ | |||
+ | TItems = class(TObjectList<TItem>); | ||
+ | </pre> | ||
+ | |||
+ | To be able to serialize it, we need to declare a converter class, and '''associate''' this class with our collection using Converter attribute: | ||
+ | |||
+ | <pre class="brush:delphi"> | ||
+ | type | ||
+ | TItemsConverter = class(TConverter) | ||
+ | public | ||
+ | function GetReadMode: TReadMode; override; | ||
+ | procedure Write(S: TSerializer; const V); override; | ||
+ | procedure Read(D: TDeserializer; var V); override; | ||
+ | end; | ||
+ | |||
+ | [Converter(TItemsConverter)] | ||
+ | TItems = class(TObjectList<TItem>); | ||
+ | </pre> | ||
+ | |||
+ | The converter can be implemented like this: | ||
+ | |||
+ | <pre class="brush:delphi"> | ||
+ | function TItemsConverter.GetReadMode: TReadMode; | ||
+ | begin | ||
+ | Result := rmFillRead; // Indicate fill-read mode. | ||
+ | end; | ||
+ | |||
+ | procedure TItemsConverter.Read(D: TDeserializer; var V); | ||
+ | var | ||
+ | lst: TItems; | ||
+ | begin | ||
+ | lst := TItems(V); // Since we work in fill-read mode, | ||
+ | // V already contains a reference to | ||
+ | // TItems instance; we should not | ||
+ | // create it here. | ||
+ | lst.Clear; | ||
+ | |||
+ | D.BeginArray; | ||
+ | while D.HasNext do | ||
+ | lst.Add(D.Value<TItem>); | ||
+ | D.EndArray; | ||
+ | end; | ||
+ | |||
+ | procedure TItemsConverter.Write(S: TSerializer; const V); | ||
+ | var | ||
+ | lst: TItems; | ||
+ | i: Integer; | ||
+ | begin | ||
+ | S.BeginArray; | ||
+ | lst := TItems(V); | ||
+ | for i := 0 to lst.Count - 1 do | ||
+ | S.Value(lst[i]); | ||
+ | S.EndArray; | ||
+ | end; | ||
+ | </pre> | ||
+ | |||
+ | As can be seen, GetReadMode returns rmFillRead, which indicates that de-serializer should use fill-read mode. This means that no new TItems instance is created during de-serialization. This mode is especially usefull for collection like classes, because they usually owned by some other objects. For example, lets look at the following class: | ||
+ | |||
+ | <pre class="brush:delphi"> | ||
+ | type | ||
+ | TOwner = class | ||
+ | private | ||
+ | FItems: TItems; | ||
+ | public | ||
+ | constructor Create; | ||
+ | destructor Destroy; override; | ||
+ | property Items: TItems read FItems; | ||
+ | end; | ||
+ | |||
+ | constructor TOwner.Create; | ||
+ | begin | ||
+ | inherited Create; | ||
+ | FItems := TItems.Create; | ||
+ | end; | ||
+ | |||
+ | destructor TOwner.Destroy; | ||
+ | begin | ||
+ | FItems.Free; | ||
+ | inherited; | ||
+ | end; | ||
+ | </pre> | ||
+ | |||
+ | As can be seen, TItems collection is owned by TOwner class. TOwner expose Items as read-only property, and thus, there no way to replace collection as a whole. Only collection content can be changed. | ||
+ | |||
+ | But, fortunately, TOwner class can be serialized and de-serialized, because TItems collection has fill-read mode converter: | ||
+ | |||
+ | <pre class="brush:delphi"> | ||
+ | var | ||
+ | src, dst: TOwner; | ||
+ | stream: TStream; | ||
+ | s: TBinarySerializer; | ||
+ | d: TBinaryDeserializer; | ||
+ | begin | ||
+ | stream := TMemoryStream.Create; | ||
+ | try | ||
+ | src := TOwner.Create; | ||
+ | src.Items.Add(TItem.Create); | ||
+ | src.Items.Add(TItem.Create); | ||
+ | src.Items.Add(TItem.Create); | ||
+ | |||
+ | s := TBinarySerializer.Create(stream); | ||
+ | s.Value(src); | ||
+ | s.Free; | ||
+ | |||
+ | stream.Position := 0; | ||
+ | |||
+ | d := TBinaryDeserializer.Create(stream); | ||
+ | dst := d.Value<TOwner>; | ||
+ | d.Free; | ||
+ | finally | ||
+ | stream.Free; | ||
+ | end; | ||
+ | end; | ||
+ | </pre> | ||
+ | |||
+ | Please use the following link for more information about fill-read mode: | ||
+ | [http://www.lmd.de/downloads/tutorials/serialization/index.html?fill-read_mode.htm Fill-Read Mode] |
Latest revision as of 17:02, 27 May 2015
<< Back to Overview page
NG SerializerPack
How to serialize collections
Serializing collections, including custom or standard collections (like TList) requires writing custom converters. For example, lets consider the folowing collection declaration:
type TItem = class public X: Integer; Y: Integer; end; TItems = class(TObjectList<TItem>);
To be able to serialize it, we need to declare a converter class, and associate this class with our collection using Converter attribute:
type TItemsConverter = class(TConverter) public function GetReadMode: TReadMode; override; procedure Write(S: TSerializer; const V); override; procedure Read(D: TDeserializer; var V); override; end; [Converter(TItemsConverter)] TItems = class(TObjectList<TItem>);
The converter can be implemented like this:
function TItemsConverter.GetReadMode: TReadMode; begin Result := rmFillRead; // Indicate fill-read mode. end; procedure TItemsConverter.Read(D: TDeserializer; var V); var lst: TItems; begin lst := TItems(V); // Since we work in fill-read mode, // V already contains a reference to // TItems instance; we should not // create it here. lst.Clear; D.BeginArray; while D.HasNext do lst.Add(D.Value<TItem>); D.EndArray; end; procedure TItemsConverter.Write(S: TSerializer; const V); var lst: TItems; i: Integer; begin S.BeginArray; lst := TItems(V); for i := 0 to lst.Count - 1 do S.Value(lst[i]); S.EndArray; end;
As can be seen, GetReadMode returns rmFillRead, which indicates that de-serializer should use fill-read mode. This means that no new TItems instance is created during de-serialization. This mode is especially usefull for collection like classes, because they usually owned by some other objects. For example, lets look at the following class:
type TOwner = class private FItems: TItems; public constructor Create; destructor Destroy; override; property Items: TItems read FItems; end; constructor TOwner.Create; begin inherited Create; FItems := TItems.Create; end; destructor TOwner.Destroy; begin FItems.Free; inherited; end;
As can be seen, TItems collection is owned by TOwner class. TOwner expose Items as read-only property, and thus, there no way to replace collection as a whole. Only collection content can be changed.
But, fortunately, TOwner class can be serialized and de-serialized, because TItems collection has fill-read mode converter:
var src, dst: TOwner; stream: TStream; s: TBinarySerializer; d: TBinaryDeserializer; begin stream := TMemoryStream.Create; try src := TOwner.Create; src.Items.Add(TItem.Create); src.Items.Add(TItem.Create); src.Items.Add(TItem.Create); s := TBinarySerializer.Create(stream); s.Value(src); s.Free; stream.Position := 0; d := TBinaryDeserializer.Create(stream); dst := d.Value<TOwner>; d.Free; finally stream.Free; end; end;
Please use the following link for more information about fill-read mode: Fill-Read Mode