unit XData.Json.CriteriaResultConverter;

interface

uses
  Generics.Collections, System.Rtti, System.SysUtils, Variants,
  Aurelius.Criteria.Base,
  Bcl.Json.Converters,
  Bcl.Json.Reader,
  Bcl.Json.Writer;

type
  TJsonCriteriaResultConverterFactory = class;

  TJsonCriteriaResultConverter = class(TInterfacedObject, IJsonTypeConverter)
  private
    {$IFDEF AUTOREFCOUNT}[Weak]{$ENDIF}
    FConverters: TJsonConverters;
    FVariantConverter: IJsonTypeConverter;
  strict protected
    procedure WriteJson(const Writer: TJsonWriter; const Value: TValue);
    procedure ReadJson(const Reader: TJsonReader; var Value: TValue);
  public
    constructor Create(AConverters: TJsonConverters);
  end;

  TJsonCriteriaResultConverterFactory = class(TInterfacedObject, IJsonConverterFactory)
  strict protected
    function CreateConverter(AConverters: TJsonConverters; const ATypeToken: TTypeToken): IJsonTypeConverter;
  end;

implementation

uses
  DB;

{ TJsonCriteriaResultConverter }

constructor TJsonCriteriaResultConverter.Create(AConverters: TJsonConverters);
begin
  FConverters := AConverters;
  FVariantConverter := FConverters.Get(TypeInfo(Variant));
end;

procedure TJsonCriteriaResultConverter.ReadJson(const Reader: TJsonReader;
  var Value: TValue);
var
  Props: TObjectList<TCriteriaResultProp>;
  Name: string;
begin
  Props := TObjectList<TCriteriaResultProp>.Create;
  try
    Reader.ReadBeginObject;
    while Reader.HasNext do
    begin
      Name := Reader.ReadName;
      if Name = '$metadata' then
        Reader.SkipValue
      else
        case Reader.Peek of
          TJsonToken.Boolean:
            Props.Add(TCriteriaResultProp.Create(Name, ftBoolean, Reader.ReadBoolean));
          TJsonToken.Text:
            Props.Add(TCriteriaResultProp.Create(Name, ftString, Reader.ReadString));
          TJsonToken.Number:
            if Reader.IsFloat then
              Props.Add(TCriteriaResultProp.Create(Name, ftFloat, Reader.ReadDouble))
            else
            if Reader.IsInt64 then
              Props.Add(TCriteriaResultProp.Create(Name, ftLargeint, Reader.ReadInt64))
            else
              Props.Add(TCriteriaResultProp.Create(Name, ftInteger, Reader.ReadInteger));
          TJsonToken.Null:
            begin
              Props.Add(TCriteriaResultProp.Create(Name, ftUnknown, NULL));
              Reader.ReadNull;
            end;
        end;
    end;
    Reader.ReadEndObject;
    Value := TCriteriaResult.Create(Props);
  except
    Props.Free;
    raise;
  end;
  FConverters.ObjectCreated(Value.AsObject);
end;

procedure TJsonCriteriaResultConverter.WriteJson(const Writer: TJsonWriter;
  const Value: TValue);
var
  I: Integer;
  Res: TCriteriaResult;
begin
  Res := Value.AsType<TCriteriaResult>;
  Writer.WriteBeginObject;
  for I := 0 to Res.Count - 1 do
  begin
    Writer.WriteName(Res.PropNames[I]);
    FVariantConverter.WriteJson(Writer, TValue.From<Variant>(Res.Values[I]));
  end;
  Writer.WriteEndObject;
end;

{ TJsonCriteriaResultConverterFactory }

function TJsonCriteriaResultConverterFactory.CreateConverter(
  AConverters: TJsonConverters; const ATypeToken: TTypeToken): IJsonTypeConverter;
begin
  if ATypeToken.TypeInfo = TypeInfo(TCriteriaResult) then
    Result := TJsonCriteriaResultConverter.Create(AConverters)
  else
    Result := nil;
end;

end.
