Introducing JsonToDelphiClass

JSON format spread over the years and became preferred lightweight protocol for data exchange. Delphi natively supports JSON via the classes in the System.JSON unit. But dealing with TJsonObject requires an intermediate layer for mapping JSON values to our business logic.
Recent Delphi releases make it easier to load JSON data to a specific BL class with the help of TJson.JsonToObject. But in all cases the developer should manually create these BL classes which can be time consuming operation. The missing part is the JSON Data binding wizard.

Json to class generators are not something new. There is already JsonToCSharpClass / json2csharp but these cover C#. I couldn’t find such class generator for Delphi.

So the JsonToDelphiClass was born. It analyses given JSON string, visualizes it in an easy to read way (in a treeview) and generates corresponding delphi classes (business objects). Just what XML Data binding wizard does for XML.

Main features:

  1. Build entirely on the RTL (no external dependencies) so it’s cross-platform;
  2. Accepts any valid JSON string, no matter how complex the object is;
    You can validate JSON strings online.
  3. Visualizes the structure of the JSON objects in a treeview;
  4. Generates complete delphi unit (declaration and implementation), based on the JSON string input;
  5. Automatically prefixes reserved Delphi words with “&” (ampersand);
  6. Automatically prevents class name collisions by using numeric suffixes;
  7. * Blocks unit generation if the JSON string contains empty Array;
  8. Adds support code to automatically destroy complex sub types. So you don’t have to manage subobject’s lifetime manually;
  9. ** Uses TArray<T> to represent lists;
  10. Adds helper serialization/deserialization functions;
  11. Serialization and deserialization results in the same JSON structure!
  12. Automatically detects date/datetime parts and maps them to TDate/TDateTime (as long as dates are ISO8601 compliant);
  13. Maps all numbers to Double;
  14. Maps true/false values to Boolean;
  15. Allows you to change property names (keys);
  16. Allows you to change the names of the stub classes;
  17. All fields in stub classes are sorted alphabetically;
  18. Supports JSON pretty print to format the input string;
  19. Simple and responsive GUI;
  20. *** Automatic check for update, based on ITask (Parallel Programming Library)!
  21. It’s open source! You can find the source code and binary releases on GitHub;
  22. The program uses MadExcept to report unhanded exceptions;

* If the JSON array is empty the contained type is unknown. Unit generation works only with known and supported types.
** This is because serialization of TList<T> adds “noise” i.e. includes internal properties that did not exist in the original JSON string.
*** The releases of JsonToDelphiClass (source and binaries) are public and reside on GitHub. The update unit uses GitHub’s REST API to enumerate tags/releases.

I decided to use Firemonkey (XE7). It was a big challenge, because I faced many problems! So this slowed me down because I had to submit QCs i find workarrounds:

QC129521, QC129539, QC129540, QC129541, QC129544, QC129552.

Hopefully these QCs will be fixed soon.

Still there are a few nasty bugs, but it’s hard to reproduce them for QC reports:

  • At first show the main Popup menu is badly clipped:PopupMenuBug
  • The Treeview changes items’ height when you press Visualize button two or more times;
  • One possible issue with MadExcept and Report Resource Leaks feature;

If I have free time I’ll dig into these bugs.

But the final result looks good and should compile for MAC OS!

BTW uGitHub.pas is generated by the JsonToDelphiClass itself 🙂

The idea of automatic update check is not something new to me. I’ve done this hundreds of times before. But this is the first time that I use GitHub for program update purposes. I am pleasantly surprised from what GitHub offers – Releases and REST interface! Which made me play with Delphi REST components. Nice!

This program uses *very simple* check for update mechanism. The user is only notified for new version availability, but do not automatically download the new version!

Git REST API require the use of SSL. This is why I needed to use OpenSSL binaries. And by the way I finally got it to know why the first call to idHTTP.GET is soooo slow (about 3 sec.) with SSL: LoadOpenSSLLibrary calls InitializeRandom. The next call to _RAND_Screen() blocks for about 3 seconds!

This is why the program cannot terminate immediately.

Update checker uses the new Parallel Programming library and especially ITask. The program demonstrates basic ITask usage, GUI responsiveness and clean termination.

The UI is simple and User experience is good enough:

  • The UI don’t freeze;
  • The treeview supports a PopUp menu that allows you to rename properties and classes. The F2 key also works;
  • The JSON string memo supports JSON prettify feature, available through mouse double click or from the context menu;
  • The Save Dialog automatically receives the name of the target Delphi unit;
  • The Main Form is re-sizable and have minimum constraints set;
  • You can horizontally resize the main panels using the splitter;
  • You are notified if there is newer version. An additional form shows more details:

UpdateScreen

Simple usage:

  1. Paste a valid JSON string into the memo. You can prettify it with double click inside the memo;
  2. Press the “Visualize” button. On the left you will see the future class structure – properties and types;MainScreen
  3. Edit the structure if you need to. You can rename properties and classes with right-click or F2;ChangePopupMenu
  4. Press “Preview Unit” button to see the generated unit;PreviewUnitScreen
  5. Save the generated unit;

IMPORTANT: The generated unit is compatible with Delphi XE5 or later. Backward compatibility is planned.

MEANWHILE if you need to compile the generated units with older versions, please remove/comment out the ToJsonString/FromJsonString and remove Rest.Json and System.Json units from the USES clause.

EXTERNAL REVIEW:

I was pleasantly surprised to find a review of JsonToDelphiClass here: JSON To Delphi Class Generator Wizard For Delphi XE7 Firemonkey On Android And IOS. Thanks to FMX Express team!

PROJECT INFORMATION:

GitHub Project link: https://github.com/PKGeorgiev/Delphi-JsonToDelphiClass

GitHub Releases link: https://github.com/PKGeorgiev/Delphi-JsonToDelphiClass/releases

The archive JsonToDelphiClass-A.BC.zip contains the release version of the program along with libeay32.dll and ssleay32.dll. I plan to play with UPX to further reduce exe size.

Feel free to report any problems/suggestions using GitHub’s facilities.

Enjoy!

27 thoughts on “Introducing JsonToDelphiClass

  1. So if I understand correctly I can deserialise a JSON string into the object created from the unit via TJSONUnMarshal, work on the BL object and once done I can serialise the object via TJSONMarshal

    Is that correct ?

  2. Thank you for this great tools.
    I have one question :

    This Json code is nicely converted into Delphi class.

    {“version”:”1.0.0″,”action”:”no_action”,”master”:{“code”:”0001″,”id”:1,”list”:[{“nom”:”denis”,”garcon”:true},{“nom”:”anne”,”garcon”:false},{“nom”:”steph”,”garcon”:true}]}}

    Is it possible to generate “List” property as a TList instead of a TArray ?

    I’m asking this because it will be more convenient to add , remove… items to the list.

    Thank you

    Ps : uSaveUnitForm need to be modified because btnSave does nothing at all

    procedure TSaveUnitForm.btnSaveClick(Sender: TObject);
    begin
    if sd.Execute then
    begin
    memo1.Lines.SaveToFile(sd.FileName);
    end;
    end;

  3. Hello, Stephane! RTL’s JSON marshaler will add some “noise” when converting a TList object to JSON string i.e. it will include some internal properties. This would make the JSON string incompatible/unreadable by other consumers. That’s why I chose to use TArray since it serializes as expected and according to the JSON specifications.
    I know it is inconvenient. May be I can plan to add an option to choose the type of arrays (TArray/TList) in future versions.
    It seems I didn’t synced the last source code to GitHub. I’ll do it soon. Thanks!

  4. Hi. Thanks. Problems XE8

    FromJsonString(json); function run

    Error

    raised exception class $C0000005 with message ‘access violation at 0x0040798c: read of address 0x595b5e5b’.

  5. Hello Ali,
    In XE8 there are some changes in JSON’s handling and conversion. Can you please post your JSON string that causes problems?

  6. Hi and thanks..
    I have one problem when my json have array..
    when i run the function FromJsonString(json) i get this error:
    Project Project1.exe raised exception class EConversionError with message ‘Cannot set value for field value as it is expected to be an array instead of string’.
    json example..
    {
    “issues”: [
    {
    “id”: 1,
    “name”: “test 1”,
    “custom_fields”: [
    {
    “id”: 100,
    “name”: “status”,
    “value”: “open”
    },
    {
    “id”: 101,
    “name”: “Version”,
    “value”: “1.0”
    }
    ]
    },
    {
    “id”: 2,
    “name”: “test 2”,
    “custom_fields”: [
    {
    “id”: 100,
    “name”: “status”,
    “value”: “close”
    },
    {
    “id”: 101,
    “name”: “Version”,
    “value”: “1.1”
    }
    ]
    }
    ]
    }

    Sorry for my english..

  7. Hi, I found the problem ..
    I’m trying to create a class starting the JSON that Redmine offers me.
    The redmine has an array called custom_fields that can be the following:
    {“id”: 1, “name”: “sprint”, “value”, “1”}
    or
    {“id”: 1, “name”: “sprint”, “value”: [“1”, “2”]}

  8. Hello Cassiano,
    My tests with XE7/XE8 (with latest updates) did not show any problems with FromJsonString using your JSON string. Which Delphi version do you use?
    Have you validated your JSON string (http://jsonlint.com/)?

  9. The tool does not seem to support Hyphens? Is this by design?

    I used an online JSON validator to verify the JSON and it passed without a problem?

    Sorry, i may be missing something obvious as I am new to working with JSON

  10. Hello,
    Hyphens in key names are not allowed because in Delphi property names cannot contain Hyphens.

  11. Very Very Goooooooooooooooood tool!

    btw, are you promoting MaxException 🙂

    because I think you disabled the SAVE button on purpose. to rebuilt the project, one need to
    buy MaxException.

    Thanks anyway for such a good tool and it will benefit the whole delphier.

  12. encountered two issues:

    1) tool can identify date value and makes it TDate type, but after calling FromJSONSTring, the value is 0 while the value in original JSON is “2016-0102”

    2) maps all the numbers to Extended instead of Double

  13. Hi Petar

    Really great tool; it saves a lot of time.

    One problem is that only OBJECTS (= classes) can be created. We have several JSON-Strings that only contain an array of objects (e.g.
    [
    {“EventType”:49,”Code”:”234″,”EventDate”:”20050202″, “Result”:1},
    {“EventType”:48,”Code”:”0120″,”EventDate”:”20130201″, “Group”:”g1″}
    ]
    This string cannot be converted, as it does not define a base-object, but only item-objects.
    As a workaround I wrap the string with {“items”: }, then I can access each item via the items property, which is generated by your tool automatically.

    Is there an easier ( and more intuitive) way of handling arrays?

  14. Hello Kaspar,
    This is scheduled for the next major release.

  15. How to use that class? (I know, nobody expect that question, but when I got to work with complicated JSON, I can’t go forward. I create unit using Your program (it’s great), but it generated 3 classes, and I can’t put it together to one JSON result
    I have got procedure:

    procedure TForm6.Button3Click(Sender: TObject);
    var
    test:unit7.TRootClass;
    test2:unit7.TErrors_dataClass;
    test3:unit7.TItemClass;
    begin
    test2:=unit7.TErrors_dataClass.Create;
    test2.artykul:=’art1′;
    test2.code:=’code’;
    test3:=unit7.TItemClass.Create;
    test3.errors:=’err1′;
    test3.link_plik:=’link1′;
    test3.errors_data[0].FromJsonString(test2.ToJsonString); <<<HERE
    showmessage(test3.ToJsonString);
    end;
    Which procedure I am trying to assign values 'test2'(TArray to 'test3.errors_data[0]'. When I am trying to assign values compiler shows AV (00000000) like in trying to assign to non created object. And I don't have any idea what is wrong. How to

  16. Hello Mariusz,
    At first sight test3.errors_data[0] is nil and you are calling FromJsonString method of a nil instance. That’s why you receive an AV.
    Try to create test3.errors_data instance or use test3.errors_data[0] := TYourErrors_data_class.FromJsonString(‘{…}’).

  17. Hi Marlon. Great tool.
    I have tested it with this JSON that is correct:
    ===============================================
    {
    “empleados”: [{
    “Tarjeta”:”1111″,
    “Datos”:[
    {“idempleado”:”1″},
    {“nombre”:”Juan Carlos”},
    {“apellidos1″:”Pomares”},
    {“apellidos2″:”Lopez”},
    {“password”:”1111″}
    ]
    },
    {
    “Tarjeta”:”7777″,
    “Datos”:[
    {“idempleado”:”2″},
    {“nombre”:”Salvador”},
    {“apellidos1″:”Perez”},
    {“apellidos2″:”Jarai”},
    {“password”:”1111″}
    ]
    },
    {
    “Tarjeta”:”8888″,
    “Datos”:[
    {“idempleado”:”34″},
    {“nombre”:”Pedro”},
    {“apellidos1″:”Mas”},
    {“apellidos2″:”Garcia”},
    {“password”:”1111″}
    ]
    }
    ]
    }
    ==================================================

    But generated code of TDatosClass is incomplete.

    Regards.

  18. {
    “empleados”: [
    {
    “Tarjeta”: “1111”,
    “Datos”: [
    {
    “idempleado”: “1”,
    “nombre”: “Juan Carlos”,
    “apellidos1”: “Pomares”,
    “apellidos2”: “Lopez”,
    “password”: “1111”
    }
    ]
    },
    {
    “Tarjeta”: “7777”,
    “Datos”: [
    {
    “idempleado”: “2”,
    “nombre”: “Salvador”,
    “apellidos1”: “Perez”,
    “apellidos2”: “Jarai”,
    “password”: “1111”
    }
    ]
    },
    {
    “Tarjeta”: “8888”,
    “Datos”: [
    {
    “idempleado”: “34”,
    “nombre”: “Pedro”,
    “apellidos1”: “Mas”,
    “apellidos2”: “Garcia”,
    “password”: “1111”
    }
    ]
    }
    ]
    }

  19. Hello,

    Is there any news on the compatibility of ToJsonString/FromJsonString for XE2 ?

Leave a Reply

Your email address will not be published. Required fields are marked *

*