Renders
Renders are the mechanism used by DMVCFramework to serialize your data (objects, strings, dataset, JSON structure) into a format suitable for the network transfer as a string. This process is called "Serialization" while the opposite process (create an objects from a string rapresentation) is called "Deserialization". For instance if you want to serialize a dataset you can call withing a controller
Render(LMyDataset)
and your client will get a serialized version of dataset data. By default the serialization format is JSON but this can be changed and customized according to your needs. Remember, all the serialization/deserialization activities should be happens within the controller actions.
Rendering JSON data
Here's a controller action which returns JSON data string serializing a native Delphi JSON object.
procedure TRenderSampleController.GetPerson(CTX: TWebContext);
var
LPerson: TJSONObject;
begin
LPerson := TJSONObject.Create;
try
LPerson.AddPair('FirstName', 'Daniele');
LPerson.AddPair('LastName', 'Teti');
LPerson.AddPair('DOB', ISODateToString(EncodeDate(1975, 5, 2)));
LPerson.AddPair('Married', TJSONTrue.Create);
Render(LPerson);
except
LPerson.Free; //in case of exception, avoid memory leaks
raise; //re-raise the exception
end;
end;
Rendering TDataSet
Here's a controller action which returns JSON data string serializing a TDataSet descendant.
procedure TRenderSampleController.GetCustomers_AsDataSet(CTX: TWebContext);
var
wm: TWebModule1;
begin
//On the WebModule there is a TFDConnection connected to a
//database and a TFDQuery with the corresponding select SQL
wm := GetCurrentWebModule as TWebModule1;
wm.qryCustomers.Open;
Render(wm.qryCustomers); //Renders all columns and all rows of the Query
end;
Rendering objects and list of objects
Here's a controller action which returns JSON data string serializing a native Delphi object.
procedure TRenderSampleController.GetCustomerByID(CTX: TWebContext);
var
Cust: TCustomer;
begin
Cust := TCustomer.Create;
try
Cust.Name := 'bit Time Professionals';
Cust.ContactFirst := 'Daniele';
Cust.ContactLast := 'Teti';
Cust.AddressLine1 := 'Rome Street 12';
Cust.AddressLine2 := '00100';
Cust.City := 'ROME';
{The following line renders a TObject descendant as JSON.
The object memory is automatically freed however,
you can override this behaviour using other parameters}
Render(Cust);
except
Cust.Free; //in case of exception, avoid memory leaks
raise; //re-raise the exception
end;
end;
Here's a type declaration of a native Delphi object containing a nested object and a nested object list.
uses
Generics.Collections;
type
TNested = class
private
FNestedString: String;
FNestedInteger: Integer;
public
property NestedString: String read FNestedString write FNestedString;
property NestedInteger: Integer read FNestedInteger write FNestedInteger;
end;
TParent = class
private
FParentString: String;
FNestedProperty: TNested;
FNestedList: TObjectList<TNested>;
procedure SetNestedList(const Value: TObjectList<TNested>);
public
property ParentString: String read FParentString write FParentString;
property NestedProperty: TNested read FNestedProperty write FNestedProperty;
[MapperListOf(TNested)]
property NestedList: TObjectList<TNested> read FNestedList write SetNestedList;
constructor Create; virtual;
destructor Destroy; override;
end;
The [MapperListOf(TNested)] attribute is important. This tells the DMVC Framework which class is in the list and can be auto created by the deserialization. If this attribute is missing the items of the object list would not be created even if there is data in the JSON string.
For serialization and deserialization nested objects and object lists must be created. But can be freed at runtime as we can see further down.
constructor TParent.Create;
begin
inherited;
FNestedProperty := TNested.Create;
FNestedList := TObjectList<TNested>.Create;
end;
destructor TParent.Destroy;
begin
FNestedProperty.Free;
FNestedList.Free;
inherited;
end;
procedure TParent.SetNestedList(const Value: TObjectList<TNested>);
begin
if (Value <> FNestedList) then
begin
FNestedList.Free;
FNestedList := Value;
end;
end;
Here's a controller action which returns JSON data string serializing the object from above:
procedure TRenderSampleController.GetParent(CTX: TWebContext);
var
Data: TParent;
NestedData: TNested;
begin
Data := TParent.Create;
try
Data.ParentString := 'ParentString';
Data.NestedProperty.NestedString := 'NestedString';
Data.NestedProperty.NestedInteger := 42;
//in case of an ampty NestedProperty You can do
//Data.NestedProperty.Free;
//Data.NestedProperty := nil;
NestedData := TNested.Create;
NestedData.NestedString := 'one more NestedString';
NestedData.NestedInteger := 21;
Data.NestedList.Add(NestedData);
{The following line renders a TObject descendant as JSON.
The object memory is automatically freed however,
you can override this behaviour using other parameters}
Render(Data);
except
Data.Free; //in case of exception, avoid memory leaks
raise; //re-raise the exception
end;
end;
The returning JSON string:
{
"ParentString" : "ParentString",
"NestedProperty" : {
"NestedString" : "NestedString",
"NestedInteger" : 42
},
"NestedList" : [{
"NestedString" : "one more NestedString",
"NestedInteger" : 21
}
]
}
If You want to allow "null" or "nil" of FNestedProperty You can free and nil it at runtime. If FNestedProperty is nil the srialization produces a JSON string which contains the name NestedProperty with the value null. For deserialization FNestedProperty must be created within TParent.Create. The deserialization make the free and nil. NestedProperty can't have a setter in this case.
The returning JSON string with the null value:
{
"ParentString" : "ParentString",
"NestedProperty" : null,
"NestedList" : [{
"NestedString" : "one more NestedString",
"NestedInteger" : 21
}
]
}
Attributes
[MapperJSONNaming(JSONNameLowerCase)] / [MapperJSONNaming(JSONNameUpperCase)]
This is a class level attribute which tells the DMVC serialization to lower case or upper case JSON names. Example:
type
[MapperJSONNaming(JSONNameLowerCase)]
TNested = class
private
FNestedString: String;
public
property NestedString: String read FNestedString write FNestedString;
end;
Output:
{
"nestedstring" : "one more NestedString",
}
[MapperTransient]
This is a property level attribute which tells the DMVC serialization do not serialize this property. Example:
type
TNested = class
private
FNestedString: String;
FTransient: String;
public
[MapperTransient]
property Transient: String read FTransient write FTransient;
property NestedString: String read FNestedString write FNestedString;
end;
Output:
{
"NestedString" : "one more NestedString",
}
[MapperListOf(TObject)]
This is a property level attribute for TList/TObjectList properties which tells the DMVC deserialization which class is in the list and should be deserialized. This attribute did not have any impact on serialization but is important for deserialization. Example:
type
TNested = class
private
FNestedString: String;
public
property NestedString: String read FNestedString write FNestedString;
end;
TParent = class
private
FNestedList: TObjectList<TNested>;
public
[MapperListOf(TNested)]
property NestedList: TObjectList<TNested> read FNestedList write FNestedList;
end;
[MapperJSONSer('json_name')]
This is a property level attribute which tells the DMVC serialization do use the given string as JSON name. Example:
type
TNested = class
private
FNestedString: String;
public
[MapperJSONSer('new_name')]
property NestedString: String read FNestedString write FNestedString;
end;
Output:
{
"new_name" : "one more NestedString",
}
Rendering raw data as streams
Here's a controller action which returns an image serializing a TStream descendant.
procedure TRenderSampleController.GetPersonPhotoAsStream(CTX: TWebContext);
var
LPhoto: TFileStream;
begin
LPhoto := TFileStream.Create('..\..\..\_\customer.png', fmOpenRead or fmShareDenyWrite);
ContentType := 'image/png'; // you can also use MVCProduces attribute
// LPhoto is a plain TStream descendant, so it can be rendered as usual
Render(LPhoto, True);
end;
With this method You can render all kind of data (images, pdf files, word documents, binary data ...). It is important to set the content type so the client (browser) can handle the data correctly.