unit Bcl.Json.ValueConverter;

{$I Bcl.Inc}

interface

{$IFDEF DELPHIXE6_LVL}

uses
  System.Json, Rtti, SysUtils,
  Bcl.Json.Converters,
  Bcl.Json.Reader,
  Bcl.Json.Writer;

type
  TJsonValueConverterFactory = class;

  TJsonValueConverter = class(TInterfacedObject, IJsonTypeConverter)
  strict private
    {$IFDEF AUTOREFCOUNT} [Weak] {$ENDIF}
    FConverters: TJsonConverters;
    function ReadObject(Reader: TJsonReader): TJsonObject;
    function ReadArray(Reader: TJsonReader): TJsonArray;
    function ReadElement(Reader: TJsonReader): TJsonValue;
  strict private
    procedure WriteObject(Writer: TJsonWriter; Value: TJSONObject);
    procedure WriteArray(Writer: TJsonWriter; Value: TJSONArray);
    procedure WriteElement(Writer: TJsonWriter; Value: TJSONValue);
  strict protected
    procedure WriteJson(const Writer: TJsonWriter; const Value: TValue);
    procedure ReadJson(const Reader: TJsonReader; var Value: TValue);
  public
    constructor Create(AConverters: TJsonConverters);
  end;

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

  EJsonValueConverterException = class(Exception)
  public
    constructor Create;
  end;

{$ENDIF}

implementation

{$IFDEF DELPHIXE6_LVL}

{ EJsonValueConverterException }

constructor EJsonValueConverterException.Create;
begin
  inherited Create('Cannot convert TJSONValue.');
end;

{ TJsonValueConverter }

constructor TJsonValueConverter.Create(AConverters: TJsonConverters);
begin
  FConverters := AConverters;
end;

function TJsonValueConverter.ReadArray(Reader: TJsonReader): TJsonArray;
begin
  Result := TJSONArray.Create;
  try
    Reader.ReadBeginArray;
    while Reader.HasNext do
      Result.AddElement(ReadElement(Reader));
    Reader.ReadEndArray;
  except
    Result.Free;
    raise;
  end;
end;

function TJsonValueConverter.ReadElement(Reader: TJsonReader): TJsonValue;
begin
  case Reader.Peek of
    TJsonToken.BeginObject:
      Result := ReadObject(Reader);
    TJsonToken.BeginArray:
      Result := ReadArray(Reader);
    TJsonToken.Text:
      Result := TJSONString.Create(Reader.ReadString);
    TJsonToken.Boolean:
      if Reader.ReadBoolean then
        Result := TJSONTrue.Create
      else
        Result := TJSONFalse.Create;
    TJsonToken.Null:
      begin
        Reader.ReadNull;
        Result := TJSONNull.Create;
      end;
    TJsonToken.Number:
      if Reader.IsFloat then
        Result := TJSONNumber.Create(Reader.ReadDouble)
      else
        Result := TJSONNumber.Create(Reader.ReadInt64);
  else
    raise EJsonValueConverterException.Create;
  end;
end;

function TJsonValueConverter.ReadObject(Reader: TJsonReader): TJsonObject;
var
  Name: string;
begin
  Result := TJSONObject.Create;
  try
    Reader.ReadBeginObject;
    while Reader.HasNext do
    begin
      Name := Reader.ReadName;
      Result.AddPair(Name, ReadElement(Reader));
    end;
    Reader.ReadEndObject;
  except
    Result.Free;
    raise;
  end;
end;

procedure TJsonValueConverter.ReadJson(const Reader: TJsonReader; var Value: TValue);
begin
  Value := ReadElement(Reader);
  FConverters.ObjectCreated(Value.AsObject);
end;

procedure TJsonValueConverter.WriteJson(const Writer: TJsonWriter; const Value: TValue);
begin
  WriteElement(Writer, Value.AsType<TJSONValue>);
end;

procedure TJsonValueConverter.WriteArray(Writer: TJsonWriter;
  Value: TJSONArray);
var
  I: Integer;
begin
  Writer.WriteBeginArray;
//  for I := 0 to Value.Size - 1 do
//    WriteElement(Writer, Value.Get(I));
  for I := 0 to Value.Count - 1 do
    WriteElement(Writer, Value.Items[I]);
  Writer.WriteEndArray;
end;

procedure TJsonValueConverter.WriteElement(Writer: TJsonWriter;
  Value: TJSONValue);
var
  JNumber: TJSONNumber;
begin
  if (Value = nil) or Value.Null then
    Writer.WriteNull
  else
  if Value is TJSONNumber then
  begin
    JNumber := TJSONNumber(Value);
    if Pos('.', JNumber.ToString) > 0 then
      Writer.WriteDouble(JNumber.AsDouble)
    else
      Writer.WriteInteger(JNumber.AsInt64);
  end
  else
  if Value is TJSONString then
    Writer.WriteString(TJSONString(Value).Value)
  else
  if Value is TJSONTrue then
    Writer.WriteBoolean(True)
  else
  if Value is TJSONFalse then
    Writer.WriteBoolean(False)
  else
  if Value is TJSONArray then
    WriteArray(Writer, TJSONArray(Value))
  else
  if Value is TJSONObject then
    WriteObject(Writer, TJSONObject(Value))
  else
    raise EJsonValueConverterException.Create;
end;

procedure TJsonValueConverter.WriteObject(Writer: TJsonWriter;
  Value: TJSONObject);
var
  I: Integer;
begin
  Writer.WriteBeginObject;
//  for I := 0 to Value.Size - 1 do
//  begin
//    Writer.WriteName(Value.Get(I).JSONString.Value);
//    WriteElement(Writer, Value.Get(I).JsonValue);
//  end;

  for I := 0 to Value.Count - 1 do
  begin
    Writer.WriteName(Value.Pairs[I].JSONString.Value);
    WriteElement(Writer, Value.Pairs[I].JsonValue);
  end;
  Writer.WriteEndObject;
end;

{ TJsonValueConverterFactory }

function TJsonValueConverterFactory.CreateConverter(Converters: TJsonConverters;
  const ATypeToken: TTypeToken): IJsonTypeConverter;
begin
  if ATypeToken.IsClass and ATypeToken.GetClass.InheritsFrom(TJSONAncestor) then
    Result := TJsonValueConverter.Create(Converters)
  else
    Result := nil;
end;

{$ENDIF}

end.
