Thursday, August 05, 2010

JSON to the rescue

Tired of creating boilerplate dialogs for every TDataSet i needed to edit, i developed a generic dialog with a data grid plus the basic edit buttons (add, delete). To edit a TDataset i just call a wrapper function that setup the grid with some configuration.

While most of the time i just needed to edit a single field, it was desirable to keep the ability to edit more than one field. Also it would be necessary also to set the column width for each field. So i was passing a record with the following structure to configure the dialog:


TDataDialogInfo = record
FieldNames: String;
FieldWidths: array of Integer;
Title: String;
end;

Title stores the dialog title, FieldNames store the Field names in semicolon separated string and FieldWidths the width of corresponding field. Here is the first problem: without adding another record type with the field name and width there is noway to guarantee the correspondence between the values leading to error easily.

Another problem is that there's no way in fpc to create the record on the fly so i need to declare a constant somewhere. To use a dialog with Title "Edit Test Dataset" and editing field "Name" i need to do:


const
Info: TDataDialogInfo = (
FieldNames: 'Name';
FieldWidths: nil;
Title: 'Edit Test Dataset'
);

Notice that even if i don't need/want to set the width i have to declare the record field Widths.

I used this way for some time, and is really a lot less work than creating a TForm and setting the components for each TDataset, until i needed to add an option to show more rows than the usual in the grid. How to add such option? Adding a field to the TDataDialogInfo record would make all previous defined constants unusable and i would need to fix than all. What if later i needed to add another option?

It was at this moment that i think: "How it would be good if pascal had the ability to define objects with arbitrary fields on the fly, like in JavaScript!". Well, in fact, although indirectly, is possible to do it in pascal thanks to the native fpc implementation of JSON.

So instead of using a record to pass the configuration now i use a string with a (valid) JSON definition. In the previous example (Title "Edit Test Dataset" and editing field "Name") now i do:


Info = '{"title": "Edit Test Dataset", "fields": {"name": "Name", "title": "Person"}}';

If i just need to define the field name:


Info = '"Name"';

If i want to edit two fields:


Info = '["Id", "Name"]';

And i can set different properties for each field


Info = '{"title": "Edit Test Dataset",'+
'"fields": ["Id", {"name": "Name", "title": "Person"},' +
' { "name": "Phone", "width": 100}]}';

Notice that the format of the property (in this case "fields") vary from a single string to a JSON array or object. Better: you can mix in the same property different formats! Flexibility it's your name. All of this backward compatible: i can add another property and previous definitions will still work!

1 comment:

Marcos Douglas said...

Very interesting.