Custom JSON serialization of records
By default, during interface-based service call,
record parameter or function result will be serialized with
our proprietary binary (and optimized layout) - i.e.
RecordSave functions - then encoded in Base-64, to be
stored as plain text within the JSON stream.
In fact, there are two ways of specifying a custom JSON serialization for
- When setting a custom dynamic array JSON serializer, the associated
record will also use the same
- By setting explicitly serialization callbacks for the
of the record, with the very same
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:
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
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
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); V.TimeStamp := GetCardinal(Values); V.JSON := Values; 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.