Lars Fosdal 1792 Posted April 10, 2019 I unabashedly summon the wisdom of @Uwe Raabe and hope he has some answers for me 🙂 I have a base class that wraps the to/from JSON conversion for me and also contain some other helper functions type TJsonElement = class public class function CreateFromJson<T: TJsonElement, constructor>(const aJson: string): T; class function LoadFromFile<T:TJsonElement, constructor>(const aFileName: String):T; class function PrettyFormat(const aJsonString: String; const AsHTML:Boolean = False; const UnEscapeJson:Boolean = False):String; function AsJsonString: string; end; And I am currently using TArray<T> to do lists, but it gets old writing helpers for each TArray<T> variation, so I'd like to do something like this and wrap a TArray to do all the chores of insert, add, clear, remove, delete, etc. once and for all I'd add these explicitly as methods of TJsonList. type TJsonList<T: TJsonElement, constructor> = class(TJsonElement) type JArray = TArray<T>; private FItems: JArray; function GetItem(Index: Integer): T; procedure SetItem(Index: Integer; const Value: T); public function Add: T; function Add(const aItem: T): T; procedure Remove(const aItem: T); // etc etc property Items{Index:Integer]: T read GetItem write SetItem; default; end; type TThing = class(TJsonElement) private Fprop: string; public property prop: string read Fprop write Fprop; end; // current way TThingArray = TArray<TThing>; TArrayContainer = class(TJsonElement) private Fthings: TThingArray; public property things: TThingArray read Fthings write Fthings; end; // new way TThingList = class(TJsonList<TThing>); TListContainer = class(TJsonElement) private Fthings: TThingList; public property things: TThingList read Fthings write Fthings; end; So, what is the problem? Assume that the two objects have been created and a couple of TThing elements added. TArrayContainer.ToJsonString will output { "things": [{ "prop": "A" }, { "prop": "B" }] } Without a converter/reverter, TListContainer.ToJsonString will output { "things": { "items": [{ "prop": "A" }, { "prop": "B" }] } } So, Challenge 1: Can I make a converter/reverter that will not output things as an object, but hide items and simply output an array of TThing? Challenge 2: Can I make the conversion override permanent, so that I don't have to use an attribute for every instance and descendant of TJsonList<T>? Share this post Link to post
Uwe Raabe 2057 Posted April 10, 2019 2 hours ago, Lars Fosdal said: Can I make a converter/reverter that will not output things as an object, but hide items and simply output an array of TThing? So you want the output of "new way" be the same as in "current way"? Can you provide a small but working example program producing both of the above outputs? Share this post Link to post
Lars Fosdal 1792 Posted April 10, 2019 1 hour ago, Uwe Raabe said: So you want the output of "new way" be the same as in "current way"? Can you provide a small but working example program producing both of the above outputs? Yes - that is the goal. Remain compatible with the original format, but have reusable code for lists. In theory, it is probably better to have the internal type being a TObjectList<T> to get more functionality "for free". The conversion would break if you add other properties to the TJsonList<T> class - but I can safeguard against that f.x. by sealing the class etc. Test project attached (Note that the test classes does not do proper memory management on free). JsonListTest.dpr JsonListTestType.pas JsonListTestCase.pas Output: TTestArray Original: { "things":[ { "prop":"A1" }, { "prop":"B1" } ] } TTestArray Restored: { "things":[ { "prop":"A1" }, { "prop":"B1" } ] } TTestList Original: { "things":{ "items":[ { "prop":"A2" }, { "prop":"B2" } ] } } TTestList Restored: { "things":{ "items":[ { "prop":"A2" }, { "prop":"B2" } ] } } Press Enter: JsonListTestCase.zip Share this post Link to post
Uwe Raabe 2057 Posted April 10, 2019 33 minutes ago, Lars Fosdal said: The conversion would break if you add other properties to the TJsonList<T> class - but I can safeguard against that f.x. by sealing the class etc. That would also require to change type TThingList = class(TJsonList<TThing>); into type TThingList = TJsonList<TThing>; 1 Share this post Link to post
Lars Fosdal 1792 Posted April 10, 2019 Updated JsonListTestCase.pas and removed the test data initialization from the constructor 😛 to make the restore test more credible. JsonListTestCase.pas Share this post Link to post
Lars Fosdal 1792 Posted April 10, 2019 Also - I guess a non-generic class inbetween will be needed for "is" checks. i.e. TJsonElement = class abstract ... end; TJsonList = class abstract (TJsonElement); TJsonList<T> = sealed class (TJsonList) ... end; Share this post Link to post
Uwe Raabe 2057 Posted April 10, 2019 As far as I can see for now, that is not possible with the convenience methods of TJson. You will have to (de-)serialize the list in your own code. Currently I am not aware of any way to register a converter/reverter globally. Unfortunately that requires to copy some code currently hidden in private methods. 1 Share this post Link to post
Lars Fosdal 1792 Posted April 10, 2019 Ok, Thanks for taking time to check it out! Share this post Link to post