Recent

Author Topic: RTTI and Class serialization  (Read 6643 times)

CynicRus

  • New Member
  • *
  • Posts: 49
RTTI and Class serialization
« on: March 26, 2017, 11:55:35 am »
Hello. I have the class. The properties in the class are written in a certain order. It is necessary to write in XML these properties with values, exactly in the order in which they follow each other in the description:
Code: Pascal  [Select][+][-]
  1. TRequestBody = class(TBaseRequestBody)
  2.   public
  3.     //function SaveToString(const RequestData: TDomNode): string; override;
  4.     procedure ToXML(const XMLNode: TDomNode);override;
  5.   published
  6.     property ProtocolLabel;
  7.     property ProtocolVersion;
  8.     property RequestId;
  9.     property DateTime;
  10.     property Command;
  11.   end;
  12.  

I'm trying to do this through RTTI:
Code: Pascal  [Select][+][-]
  1. procedure TRequestBody.ToXML(const XMLNode: TDomNode);
  2. var
  3.   PropList: TPropInfoList;
  4.   i: integer;
  5.   PI : PPropInfo;
  6.   PT : PTypeInfo;
  7.   xDoc: TXMLDocument;
  8.   RootNode, ParentNode, Node: TDomNode;
  9. begin
  10.   xDoc := TXMLDocument.Create;
  11.   try
  12.     RootNode := xDoc.CreateElement('RequestBody');
  13.     xDoc.AppendChild(RootNode);
  14.     RootNode := xDoc.DocumentElement;
  15.     PropList := TPropInfoList.Create(Self,tkProperties);
  16.  
  17.   for i := 0 to PropList.Count - 1 do
  18.   begin
  19.      PI := PropList.Items[i];
  20.      PT:=PI^.PropType;
  21.      //Str := Pi^.Name;
  22.     ParentNode := xDoc.CreateElement(Pi^.Name);
  23.     case PT^.Kind of
  24.     tkInteger:
  25.       Node := xDoc.CreateTextNode(IntToStr(GetOrdProp(Self,PI)));
  26.     tkSString,
  27.     tkLString,
  28.     tkAString:
  29.       Node := xDoc.CreateTextNode(GetStrProp(Self,PI));
  30.     tkFloat :
  31.       if (PT=TypeInfo(TDateTime)) then
  32.        Node := xDoc.CreateTextNode(DateTimeToStr(GetFloatProp(Self,PI)));
  33.     end;
  34.     ParentNode.AppendChild(Node);
  35.     RootNode.AppendChild(ParentNode);
  36.     //ParentNode.NodeValue:=;
  37.   end;
  38.   finally
  39.     XMLNode.AppendChild(RootNode.CloneNode(true,XMLNode.OwnerDocument));
  40.     xDoc.Free;
  41.   end;
  42. end;
In the output I need XML with the filed order like class properies order:
Code: XML  [Select][+][-]
  1. <RequestBody>
  2.     <ProtocolLabel>labelxxx</ProtocolLabel>
  3.     <ProtocolVersion>1.1</ProtocolVersion>
  4.     <RequestId>{9ED60922-23E8-4DD8-94C7-6AC2C2A82570}</RequestId>
  5.     <DateTime>26.03.2017 12:52:41</DateTime>
  6.     <Command>1</Command>
  7.   </RequestBody>
But, with the RTTI I get the output:
Code: XML  [Select][+][-]
  1. <RequestBody>
  2.     <Command>1</Command>
  3.     <DateTime>26.03.2017 12:52:41</DateTime>
  4.     <ProtocolLabel>labelxxx</ProtocolLabel>
  5.     <ProtocolVersion>1.1</ProtocolVersion>
  6.     <RequestId>{9ED60922-23E8-4DD8-94C7-6AC2C2A82570}</RequestId>
  7.   </RequestBody>
  8.  

i.e. class fields in the RTTI is sorted? How I can disable properties of the class sorting in the rtti?
« Last Edit: March 26, 2017, 12:10:02 pm by CynicRus »

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: RTTI and Class serialization
« Reply #1 on: March 26, 2017, 03:47:10 pm »
Hello. I have the class. The properties in the class are written in a certain order. It is necessary to write in XML these properties with values, exactly in the order in which they follow each other in the description:
I have saves in declaration order. Lazarus 1.6.4:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. uses SysUtils, Classes, DOM, TypInfo, XMLWrite;
  4.  
  5. type
  6.   TTest = class(TPersistent)
  7.   public
  8.     FNone: string;
  9.   published
  10.     property ProtocolLabel: string read FNone;
  11.     property ProtocolVersion: string read FNone;
  12.     property RequestId: string read FNone;
  13.     property DateTime: string read FNone;
  14.     property Command: string read FNone;
  15.   end;
  16.  
  17. procedure SavePropsToFile(ClassInfo: PTypeInfo; const Folder: string);
  18. var
  19.   PropList: PPropList;
  20.   Cnt: Integer;
  21.   i: Integer;
  22.   xDoc: TXMLDocument;
  23.   RootNode: TDomNode;
  24. begin
  25.   Cnt := GetPropList(ClassInfo, PropList);
  26.   if Cnt > 0 then
  27.   try
  28.     xDoc := TXMLDocument.Create;
  29.     try
  30.       RootNode := xDoc.AppendChild(xDoc.CreateElement(DOMString(ClassInfo^.Name)));
  31.       for i := 0 to Cnt - 1 do
  32.         RootNode.AppendChild(xDoc.CreateElement(DOMString(PropList^[i]^.Name)));
  33.       WriteXMLFile(xDoc, IncludeTrailingPathDelimiter(ExpandFileName(Folder)) +
  34.         ClassInfo^.Name + '.xml');
  35.     finally
  36.       xDoc.Free;
  37.     end;
  38.   finally
  39.     FreeMem(PropList);
  40.   end;
  41. end;
  42.  
  43. begin
  44.   SavePropsToFile(TypeInfo(TTest), '');
  45. end.

CynicRus

  • New Member
  • *
  • Posts: 49
Re: RTTI and Class serialization
« Reply #2 on: March 28, 2017, 07:00:05 pm »
Heh. But your properties references to the same internal variable FName.

ASerge

  • Hero Member
  • *****
  • Posts: 2223
Re: RTTI and Class serialization
« Reply #3 on: March 31, 2017, 06:23:04 pm »
But your properties references to the same internal variable FName.
This does not affect the result. You can take different fields for each property - the output order will match the order of property definition.

Arvur

  • New Member
  • *
  • Posts: 48
    • My GitHub
Re: RTTI and Class serialization
« Reply #4 on: February 08, 2020, 08:17:08 pm »
the output order will match the order of property definition.
Nope! TPropInfoList returns properties in alphabetical order. Lazarus 2.0.4, Windows.
Does anybody know - is this a bug or it can just be configured somehow?

Thaddy

  • Hero Member
  • *****
  • Posts: 14214
  • Probably until I exterminate Putin.
Re: RTTI and Class serialization
« Reply #5 on: February 08, 2020, 08:27:03 pm »
@Arvur
Huh?
Code: XML  [Select][+][-]
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TTest>
  3.   <ProtocolLabel/>
  4.   <ProtocolVersion/>
  5.   <RequestId/>
  6.   <DateTime/>
  7.   <Command/>
  8. </TTest>

That is in declaration order, not alphabetical.... (trunk r44128) ASerge's code is perfectly fine.
« Last Edit: February 08, 2020, 08:31:27 pm by Thaddy »
Specialize a type, not a var.

Arvur

  • New Member
  • *
  • Posts: 48
    • My GitHub
Re: RTTI and Class serialization
« Reply #6 on: February 08, 2020, 08:30:32 pm »
That's it:
TPropInfoList calls GetPropList(AObject.ClassInfo, Filter, nil) in constructor. And GetPropList is defined as follows
Quote
Function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList; Sorted: boolean = true): longint;
Thus in my case for example JSON Streaming always returns items alphabetically and this cannot be configured (((

Thaddy

  • Hero Member
  • *****
  • Posts: 14214
  • Probably until I exterminate Putin.
Re: RTTI and Class serialization
« Reply #7 on: February 08, 2020, 08:33:06 pm »
You can set Sorted parameter to false. Problem solved. You only need to set it explicitly., i.e. actually use the Sorted parameter.
« Last Edit: February 08, 2020, 08:35:02 pm by Thaddy »
Specialize a type, not a var.

Arvur

  • New Member
  • *
  • Posts: 48
    • My GitHub
Re: RTTI and Class serialization
« Reply #8 on: February 08, 2020, 08:34:42 pm »
@Thaddy
Quote
You can set boolean to false. Problem solved. You only need to set it explicitly.
You CANNOT! If you are using TPropInfoList directly or through TJSONStreamer.

Thaddy

  • Hero Member
  • *****
  • Posts: 14214
  • Probably until I exterminate Putin.
Re: RTTI and Class serialization
« Reply #9 on: February 08, 2020, 08:39:28 pm »
Actually ASerge chose the proper one. From typinfo unit:
Code: Pascal  [Select][+][-]
  1. Function GetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList; Sorted: boolean = true): longint;
  2. Function GetPropList(TypeInfo: PTypeInfo; out PropList: PPropList): SizeInt;
  3. function GetPropList(AClass: TClass; out PropList: PPropList): Integer;
  4. function GetPropList(Instance: TObject; out PropList: PPropList): Integer;
You should not use the info list.
« Last Edit: February 08, 2020, 08:43:56 pm by Thaddy »
Specialize a type, not a var.

Arvur

  • New Member
  • *
  • Posts: 48
    • My GitHub
Re: RTTI and Class serialization
« Reply #10 on: February 08, 2020, 08:43:30 pm »
@Thaddy
I'm using TJSONStreamer.
It always returns items in alphabetical order because TPropInfoList is used in ObjectToJSON. The only way is to everride this method.

Thaddy

  • Hero Member
  • *****
  • Posts: 14214
  • Probably until I exterminate Putin.
Re: RTTI and Class serialization
« Reply #11 on: February 08, 2020, 08:46:09 pm »
@Thaddy
I'm using TJSONStreamer.
It always returns items in alphabetical order because TPropInfoList is used in ObjectToJSON. The only way is to everride this method.
Or a helper.....or an alias macro on top of your unit:
Code: Pascal  [Select][+][-]
  1. {$macro on}{$Define GetPropInfoList := GetPropList}

GetPropInfoList is not part of the typinfo unit, nor rtti unit.

But feel free to submit a report against TJSONStreamer: that one IS at fault and should respect declaration order.
« Last Edit: February 08, 2020, 08:51:05 pm by Thaddy »
Specialize a type, not a var.

Arvur

  • New Member
  • *
  • Posts: 48
    • My GitHub
Re: RTTI and Class serialization
« Reply #12 on: February 08, 2020, 08:50:30 pm »
Oh! Macro is a fresh idea! Thanks a lot :)

I suppose TJSONStreamer should have some kind of Sorted property for that.
« Last Edit: February 08, 2020, 08:56:34 pm by Arvur »

Thaddy

  • Hero Member
  • *****
  • Posts: 14214
  • Probably until I exterminate Putin.
Re: RTTI and Class serialization
« Reply #13 on: February 08, 2020, 08:52:08 pm »
I added a line to my previous reply: file a report against TJSONstreamer, because it should not touch declaration order.
Specialize a type, not a var.

Arvur

  • New Member
  • *
  • Posts: 48
    • My GitHub
Re: RTTI and Class serialization
« Reply #14 on: February 08, 2020, 08:58:01 pm »
You are right: TPropInfoList is in rttiutils. It was taken from DesignEditors Delphi unit.
Maybe Sorted property can be added to it too? Wouldn't it be better to report against TPropInfoList also?

 

TinyPortal © 2005-2018