ITDevCon 2014, Call4Papers

Android, DMVCFramework, Design Patterns, Embarcadero, Events, ITDevCon, ITDevCon2014, Programming, Projects, RTTI, bit Time Software, iOS No Comments »

ITDevCon 2014

ITDevCon 2014

Dear potential ITDevCon speaker,

As every year, I’m building the agenda for next ITDevCon that will be held next october 23th, 24th in Milan (Italy), in a new location.

This will be the 6th edition ( we’re getting conference experts J )

The call for papers are officially open right now, so if you want to propose some speeches, I’ll be glad to see it.

As usual, for the Call4Paper I need:

  • Title (for every talk)
  • Abstract (for every talk)
  • Difficulty level (for every talk. Difficulty level is a scale from 1 to 3 with the following mean: introduction, intermediate, advanced)
  • Speaker’s photo
  • Speaker’s profile

I’m looking forward to your proposal. The call4papers ends at Aug 31st, 2014 but if you have *VERY* interesting topic to propose, propose it… I’ll try to arrange the schedule and fit it in the agenda.

Send your proposal to call4paper(at)itdevcon.it.

Proposals will be evaluated and the speakers will be contacted ASAP.

This year topics will be the following:

TOPICS

  • What’s new in the latest Delphi versions

  • FireMonkey platform

  • MacOSX development

  • Android development

  • iOS development

  • Windows development

  • Mobile development (Android+iOS)

  • Develop UIs for different devices (smartphones, tablets, TV, watch etc)

  • BaaS integrations

  • LiveBindings ®

  • Delphi best practices

  • Design Patterns

  • DataSnap

  • DelphiMVCFramework

  • Arduino e/o Raspberry Pi

  • Web Application with Delphi

  • TDD and Unit Tests

  • Agile methodologies

  • ORM and other data access patterns

  • Using NoSQL databases (even to speed up your VCL software)

  • FireDAC

  • UI, UX, optimization for mobile and desktop

  • OOD/OOP

  • Real use cases for extended RTTI, Generics e AnonMethods

  • RESTful interfaces design and development

  • Architectures

Target audience

  • Software architects
  • Software developers
  • Project managers
  • IT managers
  • Trainers

The conference web site is http://www.itdevcon.it (2014 version is still under construction).

Do you want to see a particular topic or have a suggestion for ITDevCon2014? Let me know

Thanks and see you at ITDevCon 2014.

DelphiMVCFramework is now Open Source!

DMVCFramework, Delphi XE4, Delphi XE5, MVC, RTTI 3 Comments »

DelphiMVCFramework Logo

Yes, just after 1 year of development, and a couple of big projects developed with it, the DelphiMVCFramework has been published as OpenSource.

The project is on google code svn https://code.google.com/p/delphimvcframework/

I’ll talk about it at ITDevCon2013 (http://www.itdevcon.it)

In the trunk there are many samples and I’m planning to put many others.

Main DMVCFramework features

  • RESTful (RMM Level 3) compliant
  • Server side generated pages using eLua (Embedded Lua)
  • Messaging extension using STOMP (tested with Apache Apollo and Apache ActiveMQ)
  • Can be used in load balanced environment using memcached (memcached.org)
  • Fancy URL with parameter mappings
  • Integrated RESTClient
  • Works with XE4 and XE5 (tested, but should works also on XE3 and XE2)
  • Experimental support for IOCP
  • Simple, but very very simple, to use…

It contains also a lot of indipendent code that can be used in other kind of projects.

These are the most notable:

  • Mapper (convert JSON <=> Object, ObjectList <=> JSONArray, ObjectLists <=> DataSets)
  • LuaBinding (integrate Lua script into Delphi native code)
  • LuaTextFilter (convert eLua into plain Lua executable script just like JSP)
Currently DMVCFramework use Embarcadero WebBroker framework, but is not tied to it. I’m working on a version that use IOCP (that is 10 times faster than plain WebBroker) and other web libraries for Delphi.
Code is enough stable and is already used in big production system.
WARNING!
There are a couple of bugs that affect WebBroker and so, DMVCFramework.
In our system, I’ve applied some patches to solve these problems. If you need those patches, ask me (I cannot publish the full code because it is Embarcadero licensed code).
The bugs are:
http://qc.embarcadero.com/wc/qcmain.aspx?d=119779
http://qc.embarcadero.com/wc/qcmain.aspx?d=119777
And another bug that affect also DataSnap and TIdHTTPWebBrokerBridge based projects.
Code is tested on XE4 and XE5 but should works also on XE3 and XE2.

How to serialize a TList of objects with Delphi

Delphi XE2, Embarcadero, Programming, RTTI, Uncategorized 5 Comments »

Some weeks ago a customer asked to me if it is possibile serialize a TList of objects. “Hey, you should use a TObjectList for this”, I said, but he absolutely needs (I dont know why) of a TList.

This is the (simple) sample code tested with Delphi XE2 Update4. Enjoy.

  1.  
  2. unit Unit4;
  3.  
  4. interface
  5.  
  6. uses
  7.   Winapi.Windows,
  8.   Winapi.Messages,
  9.   System.SysUtils,
  10.   System.Variants,
  11.   System.Classes,
  12.   Vcl.Graphics,
  13.   Vcl.Controls,
  14.   Vcl.Forms,
  15.   Vcl.Dialogs,
  16.   Vcl.StdCtrls;
  17.  
  18. type
  19.   TForm4 = class(TForm)
  20.     Button1: TButton;
  21.     Memo1: TMemo;
  22.     procedure Button1Click(Sender: TObject);
  23.   private
  24.     { Private declarations }
  25.   public
  26.     { Public declarations }
  27.   end;
  28.  
  29.   TPerson = class
  30.   private
  31.     FName: String;
  32.     procedure SetName(const Value: String);
  33.   published
  34.     property Name: String read FName write SetName;
  35.   end;
  36.  
  37. var
  38.   Form4: TForm4;
  39.  
  40. implementation
  41.  
  42.  
  43. uses
  44.   Contnrs,
  45.   dbxjson,
  46.   dbxjsonreflect;
  47.  
  48. {$R *.dfm}
  49.  
  50. procedure TForm4.Button1Click(Sender: TObject);
  51. var
  52.   list: TList;
  53.   m: TJSONMarshal;
  54.   json: TJSONObject;
  55.   p1: TPerson;
  56.   p2: TPerson;
  57. begin
  58.   p1 := TPerson.Create;
  59.   p2 := TPerson.Create;
  60.   try
  61.     p1.Name := 'Daniele Teti';
  62.     p2.Name := 'Peter Parker';
  63.     list := TList.Create;
  64.     try
  65.       list.Add(p1);
  66.       list.Add(p2);
  67.  
  68.       m := TJSONMarshal.Create;
  69.       try
  70.         // Register a specific converter for field FList
  71.         m.RegisterConverter(TList, 'FList', function(Data: TObject; Field: String): TListOfObjects
  72.           var
  73.             l: TList;
  74.             j: integer;
  75.           begin
  76.             l := Data as TList;
  77.             SetLength(Result, l.Count);
  78.             for j := 0 to l.Count - 1 do
  79.               Result[j] := TObject(l[j]); // HardCast from pointer
  80.           end);
  81.  
  82.         json := m.Marshal(list) as TJSONObject;
  83.         try
  84.           Memo1.Lines.Text := json.tostring;
  85.         finally
  86.           json.free;
  87.         end;
  88.       finally
  89.         m.free;
  90.       end;
  91.     finally
  92.       list.free;
  93.     end;
  94.   finally
  95.     p1.free;
  96.     p2.free;
  97.   end;
  98. end;
  99.  
  100. { TPerson }
  101.  
  102. procedure TPerson.SetName(const Value: String);
  103. begin
  104.   FName := Value;
  105. end;
  106.  
  107. end.

The output is, as expected, the following:

  1. {"type":"System.Classes.TList","id":1,"fields":{"FList":[{"type":"Unit4.TPerson","id":2,"fields":{"FName":"Daniele Teti"}},{"type":"Unit4.TPerson","id":3,"fields":{"FName":"Peter Parker"}}],"FCount":2,"FCapacity":4}}

Delphi MVC Web Framework - “Hello World”

Delphi XE, Delphi XE2, Design Patterns, Embarcadero, MVC, RTTI, dorm 6 Comments »

This is the first “Hello World” for my Delphi MVC Web Framework.

program DelphiMVCWebFramework;
  1.  
  2. {$APPTYPE CONSOLE}
  3.  
  4. uses
  5.   System.SysUtils,
  6.   MVCEngine in 'MVCEngine.pas',
  7.   BaseController in 'BaseController.pas';
  8.  
  9. var
  10.   mvc: TWebMVCEngine;
  11. begin
  12.   mvc := TWebMVCEngine.Create;
  13.   mvc.AddRoute('/', procedure(Context: TWebContext)
  14.     begin
  15.       Context.Write('Hello World');
  16.     end).Start(8080);
  17.   ReadLn;
  18.   mvc.Free;
  19. end.

Features list (some of them are in the internal roadmap)

  • Completely MVC
  • Addressable with servername/controllername/actionname?par1&par2
  • Addressable with servername/controllername/actionname/par1/par2
  • Can also use anonymous methods as actions for very simple app (in the sample)
  • Really RESTful (Level 3 of the Richardson Maturity Model)
  • Fully integrable into WebBroker and DataSnap
  • Supports multiple resource rapresentations
  • Completely customizable routing using internal formatting or regex
  • Inspired to ASP.NET, Sinatra, Java Spring MVC, Java Restlet
  • Scriptable views support (Pascal, Razor, Lua)
  • Component based
  • Session support
  • ORM support using dorm
  • Classic TDataSet support
  • State server to support real loadbalancing using reverse proxy
  • Can be deployed as standalone (in the example), ISAPI dll and Windows Service
  • Fully support https
  • More to come…
  • This framework is still under development.

    Are you interested in? :-)

    UPDATE:

    The project is open source and you can fint it here

    https://code.google.com/p/delphimvcframework/

Sneak preview about DORM, The Delphi ORM

Design Patterns, Programming, RTTI 25 Comments »

My yesterday post about this busy time, have raised some interest about DORM, the Delphi ORM.

So, also if I still haven’t released any files, wish to expose some internals about DORM.

DORM is an implementation of the DataMapper design pattern written having Hibernate in mind.

It’s completely unit tested and have the following features:

  • External file mapping. (JSON format)
  • Persistence ignorance (every TObject can be persisted)
  • Support for One-One and One-Many relations (still no many-many)
  • Support for LazyLoading (you can enable§/disable lazyloading by file or by code by per-basis needs)
  • Support for IdentityMap
  • Support for custom “finder” (you can still use complex SQL if you want)
  • Complete support for CRUD
  • Transactions
  • Built in logging system to log *EVERY* sql or action performed by the framework
  • Opened to multiple data access strategies (interfaced based, not inheritance based) for use with different database (now I’ve developed the firebird one using DBX)
  • Caching for RTTI (the TSession object have a single TRttiContext holding ALL metadata)

Code is still under heavely development.

Those are 2 test-method to show the use of DORM:

  1. TPerson = class(TObject)
  2. property Phones: TdormCollection…. //implements IList
  3. end;
  4.  
  5. TPhone = classs(TObject)
  6. end;
  7.  
  8. //and now the unit tests
  9.  
  10. procedure TTestDORMHasMany.Setup;
  11. begin
  12.   Session := TSession.Create;
  13.   Session.Configure(TStreamReader.Create('dorm.conf'));
  14. end;
  15.  
  16. procedure TTestDORMHasMany.TearDown;
  17. begin
  18.   Session.Free;
  19. end;
  20.  
  21. procedure TTestDORMHasMany.TestHasManyLazyLoad;
  22. var
  23.   p: TPerson;
  24.   t: TPhone;
  25.   guid: string;
  26. begin
  27.   p := TPerson.NewPersona;  //static method. Return a fully populated TPerson object
  28.   try
  29.     t := TPhone.Create;
  30.     p.Phones.Add(t);
  31.     Session.Save(p);
  32.     guid := p.guid;  //GUIDs, or other PK types, are generated automagically by DORM. Obviously there is a specific class loaded to do this specified in the dorm.conf file)
  33.   finally
  34.     Session.Commit;
  35.   end;
  36.   Session.StartTransaction;
  37.  
  38.   // Test with lazy load ON
  39.   Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', true);
  40.   p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
  41.   try
  42.     CheckEquals(0, p.Phones.Count);
  43.   finally
  44.     Session.Commit;
  45.   end;
  46.  
  47.   Session.StartTransaction;
  48.   // Test with lazy load OFF
  49.   Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', false);
  50.   p := Session.Load(TypeInfo(TPerson), guid) as TPerson; // Without commit, AV becouse IdentityMap doesn't work properly
  51.   try
  52.     CheckEquals(1, p.Phones.Count); // Child objects are loaded
  53.   finally
  54.     Session.Commit;
  55.   end;
  56. end;
  57.  
  58. procedure TTestDORMHasMany.TestLoadHasMany;
  59. var
  60.   list: IList;
  61.   t, t1: TPhone;
  62.   p: TPerson;
  63.   guid: string;
  64. begin
  65.   p := TPerson.NewPersona;  //static method. Return a fully populated TPerson object
  66.   try
  67.     t := TPhone.Create;
  68.     t.Numero := '555-7765123';
  69.     t.Kind := 'Casa';
  70.     p.Phones.Add(t);
  71.  
  72.     t1 := TPhone.Create;
  73.     t1.Number := '555-7765123';
  74.     t1.Kind := 'Casa';
  75.     p.Phones.Add(t1);
  76.     Session.Save(p); // save Person and Phones
  77.     guid := p.guid;
  78.   finally
  79.     Session.Commit;
  80.   end;
  81.  
  82.   Session.StartTransaction;
  83.   p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
  84.   try
  85.     CheckEquals(2, p.Phones.Count);
  86.   finally
  87.     Session.Commit;
  88.   end;
  89. end;

Mapping, contained in a file called “dorm.conf”, is similar to the following:

  1. {
  2.   "persistence": {
  3.     "database_adapter": "dorm.adapter.Firebird.TFirebirdPersistStrategy",
  4.     "database_connection_string":"127.0.0.1:C:\\MyProjects\\DORM\\experiments\\dorm.fdb",
  5.     "username": "sysdba",
  6.     "password":"masterkey"
  7.     },
  8.   "config": {
  9.     "package": "dorm.bo.Person",
  10.     "logger_class_name": "dorm.loggers.FileLog.TdormFileLog"
  11.   },      
  12.   "mapping":
  13.     {      
  14.       "TPerson":
  15.       {
  16.         "table": "people",
  17.         "id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
  18.         "fields":[
  19.           {"name":"firstname", "field":"first_name", "field_type":"string", "size": 100},
  20.           {"name":"lastname", "field":"last_name", "field_type":"string", "size": 100},
  21.           {"name":"age", "field":"age", "field_type":"integer"},
  22.           {"name":"borndate", "field":"born_date", "field_type":"date"}
  23.           ],
  24.         "has_many":[{
  25.           "name": "Phones",
  26.           "class_name":"TPhone",
  27.           "child_field_name":"guid_person",
  28.           "lazy_load": false
  29.         }],
  30.         "has_one": {
  31.           "name": "car",
  32.           "class_name":"TCar",
  33.           "child_field_name":"guid_person"
  34.         }
  35.       },
  36.       "TPhone":
  37.       {
  38.         "table": "phones",
  39.         "id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
  40.         "fields":[
  41.           {"name":"number", "field":"number", "field_type":"string", "size": 100},
  42.           {"name":"kind", "field":"kind", "field_type":"string", "size": 100},
  43.           {"name":"guid_person", "field":"guid_person", "field_type":"string", "size": 100}
  44.           ]
  45.       }
  46.     }
  47. }

The PODO (Plain Old Delphi Objects) can be binded to the VCL controls with a set of MediatingView (Model-GUI-Mediator Pattern) with an Observer mechanism to mantain things in synch.

Any comments? Someone interested?

Custom Marshalling/UnMarshalling in Delphi 2010

Delphi 2010, Programming, RTTI, Uncategorized 20 Comments »

Introduction
Some days ago, Embarcadero has presented the new version of RAD Studio, 2010.
The are many new features, but you can find in a lot places around the web, so
I won’t repeat them here.

One of the things widely requested from all Delphi programmers all over the world over the past few years, including myself, is
certainly a new and more powerful RTTI.

The new system of RTTI has finally arrived, and pave the way for a large number of applications.
One area that has benefited from the new RTTI is for sure the marshaled objects.

Marshaling is defined as follows:

“In computer science, marshalling (similar to serialization) is the process of
transforming the memory representation of an object to a data format suitable for
storage or transmission. It is typically used when data must be moved between
different parts of a computer program or from one program to another.
The opposite, or reverse, of marshalling is called unmarshalling (demarshalling) (similar to deserialization).”
–WikiPedia

In Delphi 2010 the process of serialization and deserialization is handled respectively by a Marshaller and an Unmarshaller.

The built-in format for the serialization of any Delphi object is JSON.
There are 2 main classes responsible for serializing objects into JSON, both present in the unit DBXJSONReflect:
- TJSONMarshal
- TJSONUnMarshal

Let’s say you have an object defined as follow:

type
  1.   TKid = class
  2.     FirstName: String;
  3.     LastName: String;
  4.     Age: Integer;
  5.   end;

To serialize and deserialize an instance of TKid it requires the following steps:

var
  1.   Mar: TJSONMarshal;  //Serializer
  2.   UnMar: TJSONUnMarshal;  //UnSerializer
  3.   Kid: TKid;  //The Object to serialize
  4.   SerializedKid: TJSONObject;  //Serialized for of object
  5. begin
  6.   Mar := TJSONMarshal.Create(TJSONConverter.Create);
  7.   try
  8.     Kid := TKid.Create;
  9.     try
  10.       Kid.FirstName := 'Daniele';
  11.       Kid.LastName := 'Teti';      
  12.       Kid.Age := 29;      
  13.       SerializedKid := Mar.Marshal(Kid) as TJSONObject;
  14.     finally
  15.       FreeAndNil(Kid);
  16.     end;
  17.   finally
  18.     Mar.Free;
  19.   end;
  20.   //Output the JSON version of the Kid object
  21.   WriteLn(SerializedKid.ToString);  
  22.   // UnMarshalling Kid
  23.   UnMar := TJSONUnMarshal.Create;
  24.   try
  25.     Kid := UnMar.UnMarshal(SerializedKid) as TKid;
  26.     try
  27.       //now kid is the same as before marshalling
  28.       Assert(Kid.FirstName = 'Daniele');
  29.       Assert(Kid.LastName = 'Teti');
  30.       Assert(Kid.Age = 29);
  31.     finally
  32.       Kid.Free;
  33.     end;
  34.   finally
  35.     UnMar.Free;
  36.   end;
  37. end;

Simple, isn’t it?
To access the JSON string that is our object, we must call the method ToString.
The JSON representation of this object SerializedKid can be saved to file,
sent to a remote server, used by a Web page from a web service, stored on a database or sent into space (!!!).
The Delphi application re-read the JSON string, you can recreate the object as it was at the time of serialization.
But anyone with a JSON parser can still read the data in our object, even non Delphi client.
These are the advantages of having used an open format and standard.

So far the simple part …
How serialize a field differently from the default?

Suppose we add the date of birth to our TKid:

type
  1.   TKid = class
  2.     FirstName: String;
  3.     LastName: String;
  4.     Age: Integer;
  5.     BornDate: TDateTime;
  6.   end;

Serialize a TDateTime, localized and that I have in JSON string is a float, because for Delphi TDateTime is a decimal number.
If I read the data from another program Delphi, no problem, but if I wanted to read a script in JavaScript? or. NET? or Ruby?
Then I use a format “DATA” to understand, even for these languages.
The new engine provides the serialization too.
Is needed, however, to tell the Marshaller and UnMarsheller how to represent and reconstruct a particular
object field by two statements like the following:

//marshaller
  1. Marshaller.RegisterConverter(TKid, 'BornDate',
  2.   function(Data: TObject; Field: string): string
  3.   var
  4.     ctx: TRttiContext; date : TDateTime;
  5.   begin
  6.     date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType&lt;TDateTime&gt;;
  7.     Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
  8.   end);
  9.  
  10. //UnMarshaller
  11. UnMarshaller.RegisterReverter(TKid, 'BornDate',
  12.   procedure(Data: TObject; Field: string; Arg: string)
  13.   var
  14.     ctx: TRttiContext;
  15.     datetime:TDateTime;
  16.   begin
  17.     datetime := EncodeDateTime(StrToInt(Copy(Arg, 1, 4)),
  18.                                StrToInt(Copy(Arg, 6, 2)),
  19.                                StrToInt(Copy(Arg, 9, 2)),
  20.                                StrToInt(Copy(Arg, 12, 2)),
  21.                                StrToInt(Copy(Arg, 15, 2)),
  22.                                StrToInt(Copy(Arg, 18, 2)), 0);
  23.     ctx.GetType(Data.ClassType).GetField(Field).SetValue(Data, datetime);
  24.   end);

The anonymous method is called when the marshaller serializes the field ‘BornDate’ is called “Converter” while Unmarshaller anonymous method that calls when he has to reconstruct the object from the JSON string is the “Reverter”.
Thus serializing a TKid assure you that my object is readable both by Delphi from another language without loss of information.

But what happens when I have to serialize a complex type?

Suppose we extend TKid this:

type
  1.   TTeenager = class(TKid)
  2.     Phones: TStringList;
  3.     constructor Create; virtual;
  4.     destructor Destroy; virtual;
  5.   end;

We must define a Converter and a Reverter for the TStringList class.
We can do it this way:

var
  1.   Marshaller: TJSONMarshal;
  2.   UnMarshaller: TJSONUnMarshal;
  3.   Teenager: TTeenager;
  4.   Value, JSONTeenager: TJSONObject;
  5. begin
  6.   Marshaller := TJSONMarshal.Create(TJSONConverter.Create);
  7.   try
  8.     Marshaller.RegisterConverter(TTeenager, 'BornDate',
  9.       function(Data: TObject; Field: string): string
  10.       var
  11.         ctx: TRttiContext; date : TDateTime;
  12.       begin
  13.         date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType&lt;TDateTime&gt;;
  14.         Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
  15.       end);
  16.      
  17.     Marshaller.RegisterConverter(TStringList, function(Data: TObject): TListOfStrings
  18.                                               var
  19.                                                 i, count: integer;
  20.                                               begin
  21.                                                 count := TStringList(Data).count;
  22.                                                 SetLength(Result, count);
  23.                                                 for i := 0 to count - 1 do
  24.                                                   Result[i] := TStringList(Data)[i];
  25.                                               end);  //TStringList Converter
  26.     Teenager := TTeenager.CreateAndInitialize;
  27.     try
  28.       Value := Marshaller.Marshal(Teenager) as TJSONObject;
  29.     finally
  30.       Teenager.Free;
  31.     end;
  32.   finally
  33.     Marshaller.Free;
  34.   end;
  35.   // UnMarshalling Teenager
  36.   UnMarshaller := TJSONUnMarshal.Create;
  37.   try
  38.     UnMarshaller.RegisterReverter(TTeenager, 'BornDate',
  39.       procedure(Data: TObject; Field: string; Arg: string)
  40.       var
  41.         ctx: TRttiContext;
  42.         datetime: TDateTime;
  43.       begin
  44.         datetime := EncodeDateTime(StrToInt(Copy(Arg, 1, 4)),
  45.                                    StrToInt(Copy(Arg, 6, 2)),
  46.                                    StrToInt(Copy(Arg, 9, 2)),
  47.                                    StrToInt(Copy(Arg, 12, 2)),
  48.                                    StrToInt(Copy(Arg, 15, 2)),
  49.                                    StrToInt(Copy(Arg, 18, 2)), 0);
  50.         ctx.GetType(Data.ClassType).GetField(Field).SetValue(Data, datetime);
  51.       end);
  52.     UnMarshaller.RegisterReverter(TStringList, function(Data: TListOfStrings): TObject
  53.                                                var
  54.                                                  StrList: TStringList;
  55.                                                  Str: string;
  56.                                                begin
  57.                                                  StrList := TStringList.Create;
  58.                                                  for Str in Data do
  59.                                                    StrList.Add(Str);
  60.                                                  Result := StrList;
  61.                                                end);  //TStringList Reverter
  62.  
  63.     Teenager := UnMarshaller.Unmarshal(Value) as TTeenager;
  64.     try
  65.       Assert('Daniele' = Teenager.FirstName);
  66.       Assert('Teti' = Teenager.LastName);
  67.       Assert(29 = Teenager.Age);
  68.       Assert(EncodeDate(1979, 11, 4) = Teenager.BornDate);
  69.       Assert(3 = Teenager.Phones.Count);
  70.       Assert('NUMBER01'=Teenager.Phones[0]);
  71.       Assert('NUMBER02'=Teenager.Phones[1]);
  72.       Assert('NUMBER03'=Teenager.Phones[2]);
  73.     finally
  74.       Teenager.Free;
  75.     end;
  76.   finally
  77.     UnMarshaller.Free;
  78.   end;
  79. end;

There are different types of Converter and Reverter.
In the the DBXJSONReflect there are 8 types of converters:

  1.   //Convert a field in an object array
  2.   TObjectsConverter = reference to function(Data: TObject; Field: String): TListOfObjects;
  3.   //Convert a field in a strings array
  4.   TStringsConverter = reference to function(Data: TObject; Field: string): TListOfStrings;
  5.  
  6.   //Convert a type in an objects array
  7.   TTypeObjectsConverter = reference to function(Data: TObject): TListOfObjects;
  8.   //Convert a type in a strings array  
  9.   TTypeStringsConverter = reference to function(Data: TObject): TListOfStrings;
  10.  
  11.   //Convert a field in an object
  12.   TObjectConverter = reference to function(Data: TObject; Field: String): TObject;
  13.   //Convert a field in a string  
  14.   TStringConverter = reference to function(Data: TObject; Field: string): string;
  15.  
  16.   //Convert specified type in an object
  17.   TTypeObjectConverter = reference to function(Data: TObject): TObject;
  18.   //Convert specified type in a string  
  19.   TTypeStringConverter = reference to function(Data: TObject): string;

Each of them deals with a particular conversion object representation in the final serialization, in our case we will use them to convert to JSON.

Also in the DBXJSONReflect unit are defined many “Reverter” dealing with retrieving
the serialized version of the data and use it to reconstruct the object previously serialized.
Because they are complementary to the Converter, I will not copy them here.

As a final example, we derive from TProgrammer by TTeenager adding a list of Laptops in the properties.

Is therefore necessary to introduce a new pair of Converter / Reverter.
In this example I have defined all the converter and reverter in another unit in
order to have more readable code:

type
  1.   TLaptop = class
  2.     Model: String;
  3.     Price: Currency;
  4.     constructor Create(AModel: String; APrice: Currency);
  5.   end;
  6.   TLaptops = TObjectList&lt;TLaptop&gt;;
  7.   TProgrammer = class(TTeenager)
  8.     Laptops: TLaptops;
  9.     constructor Create; override;
  10.     destructor Destroy; override;
  11.     class function CreateAndInitialize: TProgrammer;
  12.   end;
  13. // Implementation code…
  14. var
  15.   Marshaller: TJSONMarshal;
  16.   UnMarshaller: TJSONUnMarshal;
  17.   Programmer: TProgrammer;
  18.   Value, JSONProgrammer: TJSONObject;
  19. begin
  20.   Marshaller := TJSONMarshal.Create(TJSONConverter.Create);
  21.   try
  22.     Marshaller.RegisterConverter(TProgrammer, 'BornDate', ISODateTimeConverter);
  23.     Marshaller.RegisterConverter(TStringList, StringListConverter);
  24.     Marshaller.RegisterConverter(TProgrammer, 'Laptops', LaptopListConverter);
  25.     Programmer := TProgrammer.CreateAndInitialize;
  26.     try
  27.       Value := Marshaller.Marshal(Programmer) as TJSONObject;
  28.     finally
  29.       Programmer.Free;
  30.     end;
  31.  
  32.     // UnMarshalling Programmer
  33.     UnMarshaller := TJSONUnMarshal.Create;
  34.     try
  35.       UnMarshaller.RegisterReverter(TProgrammer, 'BornDate', ISODateTimeReverter);
  36.       UnMarshaller.RegisterReverter(TStringList, StringListReverter);
  37.       UnMarshaller.RegisterReverter(TProgrammer, 'Laptops', LaptopListReverter);
  38.  
  39.       Programmer := UnMarshaller.Unmarshal(Value) as TProgrammer;
  40.       try
  41.         Assert('Daniele' = Programmer.FirstName);
  42.         Assert('Teti' = Programmer.LastName);
  43.         Assert(29 = Programmer.Age);
  44.         Assert(EncodeDate(1979, 11, 4) = Programmer.BornDate);
  45.         Assert(3 = Programmer.Phones.Count);
  46.         Assert('NUMBER01' = Programmer.Phones[0]);
  47.         Assert('NUMBER02' = Programmer.Phones[1]);
  48.         Assert('NUMBER03' = Programmer.Phones[2]);
  49.         Assert('HP Presario C700' = Programmer.Laptops[0].Model);
  50.         Assert(1000 = Programmer.Laptops[0].Price);
  51.         Assert('Toshiba Satellite Pro' = Programmer.Laptops[1].Model);
  52.         Assert(800 = Programmer.Laptops[1].Price);
  53.         Assert('IBM Travelmate 500' = Programmer.Laptops[2].Model);
  54.         Assert(1300 = Programmer.Laptops[2].Price);
  55.       finally
  56.         Programmer.Free;
  57.       end;
  58.     finally
  59.       UnMarshaller.Free;
  60.     end;
  61.   finally
  62.     Marshaller.Free;
  63.   end;
  64. end;

Unit CustomConverter.pas contains all needed Converters/Reverts as anon methods.

unit CustomConverter;
  1.  
  2. interface
  3.  
  4. uses
  5.   DBXJSONReflect,
  6.   MyObjects; //Needed by converter and reverter for TLaptops
  7.  
  8. var
  9.   ISODateTimeConverter: TStringConverter;
  10.   ISODateTimeReverter: TStringReverter;
  11.  
  12.   StringListConverter: TTypeStringsConverter;
  13.   StringListReverter: TTypeStringsReverter;
  14.  
  15.   LaptopListConverter: TObjectsConverter;
  16.   LaptopListReverter: TObjectsReverter;
  17.  
  18. implementation
  19.  
  20. uses
  21.   SysUtils, RTTI, DateUtils, Classes;
  22.  
  23. initialization
  24.  
  25. LaptopListConverter := function(Data: TObject; Field: String): TListOfObjects
  26. var
  27.   Laptops: TLaptops;
  28.   i: integer;
  29. begin
  30.   Laptops := TProgrammer(Data).Laptops;
  31.   SetLength(Result, Laptops.Count);
  32.   if Laptops.Count &gt; 0 then
  33.     for I := 0 to Laptops.Count - 1 do
  34.       Result[I] := Laptops[i];
  35. end;
  36.  
  37.  
  38. LaptopListReverter := procedure(Data: TObject; Field: String; Args: TListOfObjects)
  39. var
  40.   obj: TObject;
  41.   Laptops: TLaptops;
  42.   Laptop: TLaptop;
  43.   i: integer;
  44. begin
  45.   Laptops := TProgrammer(Data).Laptops;
  46.   Laptops.Clear;
  47.   for obj in Args do
  48.   begin
  49.     laptop := obj as TLaptop;
  50.     Laptops.Add(TLaptop.Create(laptop.Model, laptop.Price));
  51.   end;
  52. end;
  53.  
  54. StringListConverter := function(Data: TObject): TListOfStrings
  55. var
  56.   i, count: integer;
  57. begin
  58.   count := TStringList(Data).count;
  59.   SetLength(Result, count);
  60.   for i := 0 to count - 1 do
  61.     Result[i] := TStringList(Data)[i];
  62. end;
  63.  
  64.  
  65. StringListReverter := function(Data: TListOfStrings): TObject
  66. var
  67.   StrList: TStringList;
  68.   Str: string;
  69. begin
  70.   StrList := TStringList.Create;
  71.   for Str in Data do
  72.     StrList.Add(Str);
  73.   Result := StrList;
  74. end;
  75.  
  76. ISODateTimeConverter := function(Data: TObject; Field: string): string
  77. var
  78.   ctx: TRttiContext; date : TDateTime;
  79. begin
  80.   date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType&lt;TDateTime&gt;;
  81.   Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
  82. end;
  83.  
  84. ISODateTimeReverter := procedure(Data: TObject; Field: string; Arg: string)
  85. var
  86.   ctx: TRttiContext;
  87.   datetime :
  88.   TDateTime;
  89. begin
  90.   datetime := EncodeDateTime(StrToInt(Copy(Arg, 1, 4)), StrToInt(Copy(Arg, 6, 2)), StrToInt(Copy(Arg, 9, 2)), StrToInt
  91.       (Copy(Arg, 12, 2)), StrToInt(Copy(Arg, 15, 2)), StrToInt(Copy(Arg, 18, 2)), 0);
  92.   ctx.GetType(Data.ClassType).GetField(Field).SetValue(Data, datetime);
  93. end;
  94.  
  95. end.

Last hint…
Every serialization/unserialization process can create “warnings”.
Those warnings are collected into the “Warnings” property of the Ser/UnSer Object.

Conclusions
In this post I tried to introduce the basics of the new serialization engine in Delphi 2010.
During the next ITDevCon to be held in Italy next November 11.12, I’ll have a talk in which I will extensively talk about serialization and RTTI.
All interested smart developers are invited :-)

ITALIAN P.S.
Se qualche programmatore italiano volesse avere la versione in italiano di questo post può lasciare un commento e vedrò di accontentarlo :-)

You can find the DUnit project Source Code

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in