I am trying to include the Spring4D framework in my latest Delphi project. The collection functions for sorting and filtering are the main features.
However, I have great difficulty to serialize a JSON array to an IList<T>
where T
is a record
.
When I use the default generic.collecions
it all gets serialized perfectly. I'm using https://github.com/gruco0002/DelphiJSON for the JSON part because it's serializing records and the standard generic lists.
This is working when using the default generic collections:
result.Data := DelphiJSON<T>.Deserialize(response.Content, jsonSetting);
I hope I don't have to revert back to TDataSet
s. I have tried to instantiate the IList<T>
and add the JSON array myself.
The System.JSON.Converters
unit contains converters for standard generics - learn it to make your own converters. To convert the root array you must to add your converter to the serializer. To convert fields you can use the RTTI attribute [JsonConverter(<Your_Own_Converter_Class_Name>)]
declared in the System.JSON.Serializers
unit.
Converter example for IList<T>
:
unit Unit1;
interface
uses
System.SysUtils,
System.JSON.Serializers,
System.JSON.Converters,
System.JSON.Writers,
System.JSON.Readers,
Spring.Collections,
Spring;
type
TJsonListConverter<V> = class(TJsonConverter)
protected
function CreateInstance: IList<V>; virtual;
public
procedure WriteJson(const AWriter: TJsonWriter; const AValue: TValue;
const ASerializer: TJsonSerializer); override;
function ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo;
const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; override;
function CanConvert(ATypeInf: PTypeInfo): Boolean; override;
end;
implementation
uses
System.Rtti,
System.TypInfo,
System.JSON.Utils,
System.JSON.Types,
System.JSONConsts;
function TJsonListConverter<V>.CanConvert(ATypeInf: PTypeInfo): Boolean;
begin
Result := TJsonTypeUtils.IsAssignableFrom(ATypeInf, TypeInfo(IList<V>));
end;
function TJsonListConverter<V>.CreateInstance: IList<V>;
begin
Result := TCollections.CreateList<V>;
end;
function TJsonListConverter<V>.ReadJson(const AReader: TJsonReader;
ATypeInf: PTypeInfo; const AExistingValue: TValue;
const ASerializer: TJsonSerializer): TValue;
var
List: IList<V>;
Arr: TArray<V>;
begin
if AReader.TokenType = TJsonToken.Null then
Result := nil
else
begin
ASerializer.Populate(AReader, Arr);
if AExistingValue.IsEmpty then
List := CreateInstance
else
List := AExistingValue.AsType<IList<V>>;
List.AddRange(Arr);
Result := TValue.From(List);
end;
end;
procedure TJsonListConverter<V>.WriteJson(const AWriter: TJsonWriter;
const AValue: TValue; const ASerializer: TJsonSerializer);
var
List: IList<V>;
begin
if AValue.TryAsType(List) then
ASerializer.Serialize(AWriter, List.ToArray)
else
raise EJsonException.Create(
Format(SConverterNotMatchingType,
[AValue.TypeInfo^.Name, PTypeInfo(TypeInfo(IList<V>))^.Name]));
end;
end.
Using:
uses
System.JSON.Serializers,
Spring.Collections,
Spring,
Unit1 in 'Unit1.pas';
type
TMyRecord = record
p1, p2, p3: integer;
end;
resourcestring
MyJson =
'[{"p1":1,"p2":2,"p3":3},{"p1":4,"p2":5,"p3":6},{"p1":7,"p2":8,"p3":9}]';
begin
// create a serializer
var Serializer :=
Shared.Make<TJsonSerializer>(
TJsonSerializer.Create,
procedure(const arg: TJsonSerializer)
begin
for var Converter in arg.Converters do Converter.Free;
end
);
// add converter
Serializer.Converters.Add(TJsonListConverter<TMyRecord>.Create);
// deserialize
var MyArray := Serializer.Deserialize<IList<TMyRecord>>(MyJson);
// serialize
Writeln(Serializer.Serialize(MyArray));
Readln;
end.