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.