Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring4D Nullable<T> JSON serialization

Is there a way to get TJson.ObjectToJsonString() properly serialize a TNullableInteger field in an object?

I tried to use an attribute on the field using a JsonReflectAttribute with a TJSONInterceptor, but then the integer value, if present, was serialized as string.

like image 271
Nicko Avatar asked Feb 25 '26 15:02

Nicko


1 Answers

JSON serialization that comes with Delphi is a disaster - as already mentioned in the comment to your question the design of REST.Json prevents any customization of record serializion from what I can see.

The only way known to me is to use a TJsonSerializer from System.JSON.Serializers and add a custom converter that can handle Nullable<T> quite easily:

uses
  Spring,
  System.JSON.Serializers,
  System.JSON.Readers,
  System.JSON.Writers,
  System.Rtti;

type
  TNullableConverter = class(TJsonConverter)
  public
    function CanConvert(ATypeInf: PTypeInfo): Boolean; override;
    function ReadJson(const AReader: TJsonReader; ATypeInf: PTypeInfo;
      const AExistingValue: TValue; const ASerializer: TJsonSerializer): TValue; override;
    procedure WriteJson(const AWriter: TJsonWriter; const AValue: TValue;
      const ASerializer: TJsonSerializer); override;
  end;

function TNullableConverter.CanConvert(ATypeInf: PTypeInfo): Boolean;
begin
  Result := IsNullable(ATypeInf);
end;

function TNullableConverter.ReadJson(const AReader: TJsonReader;
  ATypeInf: PTypeInfo; const AExistingValue: TValue;
  const ASerializer: TJsonSerializer): TValue;
begin
  Result := AExistingValue;
  Result.SetNullableValue(AReader.Value.Convert(GetUnderlyingType(ATypeInf), ISO8601FormatSettings));
end;

procedure TNullableConverter.WriteJson(const AWriter: TJsonWriter;
  const AValue: TValue; const ASerializer: TJsonSerializer);
var
  LTypeInfo: PTypeInfo;
  LValue: TValue;
begin
  LTypeInfo := GetUnderlyingType(AValue.TypeInfo);
  LValue := AValue.GetNullableValue;

  if LValue.IsEmpty then
    AWriter.WriteNull
  else
    if (LTypeInfo = TypeInfo(TDate)) or (LTypeInfo = TypeInfo(TTime)) then
      AWriter.WriteValue(LValue.Convert<string>(ISO8601FormatSettings))
    else
      AWriter.WriteValue(LValue);
end;
like image 84
Stefan Glienke Avatar answered Feb 27 '26 07:02

Stefan Glienke