Reverse: Convert a JSON string into an object instance
The last blog post (Link) covered the topic of converting object instances into a JSON string. The rather common case needs you to the exact opposite: Some web service delivers a JSON string and you want to convert the data into an object instance.
The most recent versions of Delphi have become so accustomed to JSON that you can solve this topic “graphically” using either the VCL oder FMX. You can use the REST client and its associated components to create a client dataset of the data that you read from a web service.
However, we are going for the approach that let’s you design the classes that will be the blueprint for the data being read.
Let’s use a very simple example:
{ "Name" : "Holger", "Age": 18 }
An example from my dreams no less, as I am much older…
The JSON string describes a “record” with two attributes. The attributes are named “Name” and “Age”. The JSON even implicitly defines two different data types for the data, but be aware that you can also use a string to store the “18” if you wanted to. JSON does not contain a schema definition or something similar to restrict the values or define data types. One of the major differences to XML.
If you ever looked at this problem in other programming languages, the term “abstract class type” might have been used for the first time. If you were to use C#, for example, we would be able to transfer this very JSON string right away. The JSON string would be transformed into an object that would contain the attributes from the JSON, but it would lack a name. Thus, it is anonymous. As Delphi does always need a base class for every object, this is not possible (There are anonymous methods, however…).
So, being a good Delphi developer, we define the following class:
type TPerson = class private // or public or protected Name: String; Age: Integer; end;
From what we learned from the last blog post this class should work just fine in order to be used for the automatic “conversion” that we want.
Let’s write the following code, remembering that TJson also offers a method that takes JSON input and returns an object instance. Create a simple VCL Forms application with a TMemo and a TButton . Implement its click-event as follows:
procedure TForm1.Button1Click(Sender: TObject); var p : TPerson; begin p := TJson.JsonToObject<TPerson>(Memo1.Lines.Text); Showmessage( p.Age.ToString ); end;
I copied the JSON string into the TMemo and I also defined TPerson, of course.
This should work, right?
Well, it does not.
Your ShowMessage-dialog box will be empty. From what we learned how TJson works it should work fine. For some reason I have still not found the answer to, TJson.JsonToObject relies purely on properties. Basically, your object instance does not provide the attribute names to be looked up correctly. I have to be honest that I think this is rather illogical as the creating of the JSON string worked very differently and the reversal should follow the same principles.
So, changing TPerson by defining two properties…
type TPerson = class private FName: String; FAge: Integer; published property Name: String read FName write FName; property Age: Integer read FAge write FAge; end;
… it works like a charm.
Run it again and your ShowMessage-box will display “18”. There is only one thing we need to do, so we do not get hit by a memory leak:
try p := TJson.JsonToObject<TPerson>(Memo1.Lines.Text); ShowMessage( p.Age.ToString ); finally p.Free; end;
You do not need to call TPerson.Create as TJson.JsonToObject does return a fully initialized object and reserves the memory for us.
Holger, the code you reported, runs also if the object properties are declared public. Why you must declare them published? This declaration do not exists in other languages as Framework .net.
For object properties to be persisted you need meta-data that is only being created when using ‘published’. You are correct that the Delphi JSON unit also picks everything up that is defined as public. I use Delphi since version 2 and still have it in my head that I need to use published as soon as some other class wants to know something about my objects… So, you are right public does suffice. Published does create additional information for RTTI. As an aside… If you build components the published properties appear in the Object Inspector.
Holger:
Can convert any delphi component (button, memo, stringgrid….) to
json format data and save it to file.
and load the file to Json data, then convert it to component?
thanks
Mitchell Hu
//sorry foy my poor English ability.
This is called serialization and there are several approaches to this. You will need to derive from TPersistent in Delphi in order to serialize classes using the VCL, e.g. Other means for JSON are also available that use RTTI.