View Issue Details

IDProjectCategoryView StatusLast Update
0036414FPCFCLpublic2019-12-09 23:22
ReporterAndrewH Assigned ToMichael Van Canneyt  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionwon't fix 
Product Version3.3.1 
Summary0036414: [PATCH] add option for TJSONStreamer to process the properties in the declared order. Also use GetJSONInstanceType
DescriptionHi this patch adds the ability for TJSONStreamer to process an object's properties in the order which they are declared. Also in TJSONStreamer.StreamProp use GetJSONInstanceType to create the JSON objects.

This also adds a default parameter to rttiutils TPropInfoList.Create to allow using the declared order. It defaults to the old behavior.

So:

TFoo = class
published
  property ZFoo: String;
  property ABar: String;
end;

Previously
{ "ABar": "value", "ZFoo", "value" }

Now with jsoUseDeclaredOrder in Options:
{ "ZFoo", "value", "ABar": "value" }
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget-
Attached Files

Activities

AndrewH

2019-12-09 16:03

developer  

rtti_json_ordered_and_use_instance_type.patch (6,439 bytes)   
diff --git a/packages/fcl-base/src/rttiutils.pp b/packages/fcl-base/src/rttiutils.pp
index 21519c4440..b1be0ef1d8 100644
--- a/packages/fcl-base/src/rttiutils.pp
+++ b/packages/fcl-base/src/rttiutils.pp
@@ -47,7 +47,7 @@ type
     FSize: Integer;
     function Get(Index: Integer): PPropInfo;
   public
-    constructor Create(AObject: TObject; Filter: TTypeKinds);
+    constructor Create(AObject: TObject; Filter: TTypeKinds; AInDeclarationOrder: Boolean = False);
     destructor Destroy; override;
     function Contains(P: PPropInfo): Boolean;
     function Find(const AName: string): PPropInfo;
@@ -157,14 +157,14 @@ end;
 
 { TPropInfoList }
 
-constructor TPropInfoList.Create(AObject: TObject; Filter: TTypeKinds);
+constructor TPropInfoList.Create(AObject: TObject; Filter: TTypeKinds; AInDeclarationOrder: Boolean);
 begin
   if AObject <> nil then
     begin
     FCount := GetPropList(AObject.ClassInfo, Filter, nil);
     FSize := FCount * SizeOf(Pointer);
     GetMem(FList, FSize);
-    GetPropList(AObject.ClassInfo, Filter, FList);
+    GetPropList(AObject.ClassInfo, Filter, FList, not AInDeclarationOrder)
     end
   else
     begin
diff --git a/packages/fcl-json/src/fpjsonrtti.pp b/packages/fcl-json/src/fpjsonrtti.pp
index b76a0f2f9d..c039ceeea5 100644
--- a/packages/fcl-json/src/fpjsonrtti.pp
+++ b/packages/fcl-json/src/fpjsonrtti.pp
@@ -30,7 +30,8 @@ Type
                        jsoCheckEmptyDateTime,     // If TDateTime value is empty and jsoDateTimeAsString is used, 0 date returns empty string
                        jsoLegacyDateTime,         // Set this to enable old date/time formatting. Current behaviour is to save date/time as a ISO 9601 value.
                        jsoLowerPropertyNames,     // Set this to force lowercase names when streaming to JSON.
-                       jsoStreamTList             // Set this to assume that TList contains a list of TObjects. Use with care!
+                       jsoStreamTList,            // Set this to assume that TList contains a list of TObjects. Use with care!
+                       jsoUseDeclaredOrder        // Stream the properties in the order they are delcared in the object
                        );  
   TJSONStreamOptions = Set of TJSONStreamOption;
 
@@ -780,7 +781,7 @@ begin
       Result.Add('Objects', StreamTList(TList(AObject)))
     else
       begin
-      PIL:=TPropInfoList.Create(AObject,tkProperties);
+      PIL:=TPropInfoList.Create(AObject,tkProperties, jsoUseDeclaredOrder in Options);
       try
         For I:=0 to PIL.Count-1 do
           begin
@@ -1079,25 +1080,25 @@ begin
     tkUnknown :
       Error(SErrUnknownPropertyKind,[PI^.Name]);
     tkInteger :
-      Result:=TJSONIntegerNumber.Create(GetOrdProp(AObject,PI));
+      Result:=TJSONIntegerNumberClass(GetJSONInstanceType(jitNumberInteger)).Create(GetOrdProp(AObject,PI));
     tkEnumeration :
       if jsoEnumeratedAsInteger in Options then
-        Result:=TJSONIntegerNumber.Create(GetOrdProp(AObject,PI))
+        Result:=TJSONIntegerNumberClass(GetJSONInstanceType(jitNumberInteger)).Create(GetOrdProp(AObject,PI))
       else
-        Result:=TJSONString.Create(GetEnumName(PT,GetOrdProp(AObject,PI)));
+        Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(GetEnumName(PT,GetOrdProp(AObject,PI)));
     tkFloat :
       if (PT=TypeInfo(TDateTime)) and (jsoDateTimeAsString in Options) then
         Result:=FormatDateProp(GetFloatProp(AObject,PI))
       else
-        Result:=TJSONFloatNumber.Create(GetFloatProp(AObject,PI));
+        Result:=TJSONFloatNumberClass(GetJSONInstanceType(jitNumberFloat)).Create(GetFloatProp(AObject,PI));
     tkSet :
       If jsoSetAsString in Options then
-        Result:=TJSONString.Create(GetSetProp(AObject,PI,jsoSetBrackets in Options))
+        Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(GetSetProp(AObject,PI,jsoSetBrackets in Options))
       else
         begin
         PT:=GetTypeData(PT)^.CompType;
         S:=GetOrdProp(AObject,PI);
-        Result:=TJSONArray.Create;
+        Result:=TJSONArrayClass(GetJSONInstanceType(jitArray)).Create;
         try
           for i:=0 to 31 do
             if (i in TSet(S)) then
@@ -1111,25 +1112,25 @@ begin
         end;
         end;
     tkChar:
-      Result:=TJSONString.Create(Char(GetOrdProp(AObject,PI)));
+      Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(Char(GetOrdProp(AObject,PI)));
     tkSString,
     tkLString,
     tkAString:
-      Result:=TJSONString.Create(GetStrProp(AObject,PI));
+      Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(GetStrProp(AObject,PI));
     tkWString :
-      Result:=TJSONString.Create(GetWideStrProp(AObject,PI));
+      Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(GetWideStrProp(AObject,PI));
     tkVariant:
       Result:=StreamVariant(GetVariantProp(AObject,PI));
     tkClass:
       Result:=StreamClassProperty(GetObjectProp(AObject,PI));
     tkWChar :
-      Result:=TJSONString.Create(WideChar(GetOrdProp(AObject,PI)));
+      Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(WideChar(GetOrdProp(AObject,PI)));
     tkBool :
-      Result:=TJSONBoolean.Create(GetOrdProp(AObject,PropertyInfo)<>0);
+      Result:=TJSONBooleanClass(GetJSONInstanceType(jitBoolean)).Create(GetOrdProp(AObject,PropertyInfo)<>0);
     tkInt64 :
-      Result:=TJSONInt64Number.Create(GetOrdProp(AObject,PropertyInfo));
+      Result:=TJSONInt64NumberClass(GetJSONInstanceType(jitNumberInt64)).Create(GetOrdProp(AObject,PropertyInfo));
     tkQWord :
-      Result:=TJSONFloatNumber.Create(GetOrdProp(AObject,PropertyInfo));
+      Result:= TJSONQWordNumberClass(GetJSONInstanceType(jitNumberQWord)).Create(GetOrdProp(AObject,PropertyInfo));
     tkObject :
       Result:=ObjectToJSON(GetObjectProp(AObject,PropertyInfo));
     tkArray,
@@ -1141,9 +1142,9 @@ begin
     tkMethod :
       Error(SErrUnsupportedPropertyKind,[PI^.Name]);
     tkUString :
-      Result:=TJSONString.Create(GetWideStrProp(AObject,PI));
+      Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(GetWideStrProp(AObject,PI));
     tkUChar:
-      Result:=TJSONString.Create(UnicodeChar(GetOrdProp(AObject,PI)));
+      Result:=TJSONStringClass(GetJSONInstanceType(jitString)).Create(UnicodeChar(GetOrdProp(AObject,PI)));
   end;
   If Assigned(FOnStreamProperty) then
     FOnStreamProperty(Self,AObject,PI,Result);

Michael Van Canneyt

2019-12-09 20:41

administrator   ~0119715

You should never rely on the order in a JSON object. The order depends on the hash algorithm used and can change any moment. So I will not add this.

Issue History

Date Modified Username Field Change
2019-12-09 16:03 AndrewH New Issue
2019-12-09 16:03 AndrewH File Added: rtti_json_ordered_and_use_instance_type.patch
2019-12-09 20:40 Michael Van Canneyt Assigned To => Michael Van Canneyt
2019-12-09 20:40 Michael Van Canneyt Status new => assigned
2019-12-09 20:41 Michael Van Canneyt Status assigned => resolved
2019-12-09 20:41 Michael Van Canneyt Resolution open => won't fix
2019-12-09 20:41 Michael Van Canneyt FPCTarget => -
2019-12-09 20:41 Michael Van Canneyt Note Added: 0119715