In fact, there are two ways of specifying a custom JSON serialization for
record:
- When setting a custom dynamic array JSON serializer, the associated
record will also use the same Reader and
Writer callbacks;
- By setting explicitly serialization callbacks for the TypeInfo()
of the record, with the very same TTextWriter.
RegisterCustomJSONSerializer method.
For instance, you can serialize the following record definition:
TSQLRestCacheEntryValue = record
ID: integer;
TimeStamp: cardinal;
JSON: RawUTF8;
end;
With the following code:
TTextWriter.RegisterCustomJSONSerializer(TypeInfo(TSQLRestCacheEntryValue),
TTestServiceOrientedArchitecture.CustomReader,
TTestServiceOrientedArchitecture.CustomWriter);
The expected format will be as such:
{"ID":1786554763,"TimeStamp":323618765,"JSON":"D:\TestSQL3.exe"}
Therefore, the writer callback could be:
class procedure TTestServiceOrientedArchitecture.CustomWriter( const aWriter: TTextWriter; const aValue); var V: TSQLRestCacheEntryValue absolute aValue; begin aWriter.AddJSONEscape(['ID',V.ID,'TimeStamp',Int64(V.TimeStamp),'JSON',V.JSON]); end;
In the above code, the cardinal field named
TimeStamp is type-casted to a Int64: in fact, as
stated by the documentation of the AddJSONEscape method, an
array of const will handle by default any cardinal as
an integer value (this is a limitation of the Delphi
compiler). By forcing the type to be an Int64, the expected
cardinal value will be transmitted, and not a wrongly negative
versions for numbers > $7fffffff.
On the other side, the corresponding reader callback would be like:
class function TTestServiceOrientedArchitecture.CustomReader(P: PUTF8Char;
var aValue; out aValid: Boolean): PUTF8Char;
var V: TSQLRestCacheEntryValue absolute aValue;
Values: TPUtf8CharDynArray;
begin
result := JSONDecode(P,['ID','TimeStamp','JSON'],Values);
if result=nil then
aValid := false else begin
V.ID := GetInteger(Values[0]);
V.TimeStamp := GetCardinal(Values[1]);
V.JSON := Values[2];
aValid := true;
end;
end;
Note that the callback signature used for records matches the one used for dynamic arrays serializations - see this article - as it will be shared between the two of them.
Even if older versions of Delphi are not able to generate the
needed RTTI information for such serialization, the mORMot framework
offers a common way of implementing any custom serialization of records.
When records are used as Data Transfer
Objects within services (which is a good idea in common SOA
implementation patterns), such a custom serialization format can be handy, and
makes more natural service consumption with AJAX clients.
See this commit about the feature.
