Search code examples
jsondelphidelphi-xe5

Delphi pointer variable required


I am trying to create a global procedure which can append a JSON field to a main JSON object dynamically.

procedure TJSONAdapter.addField(Key, Field: String);
var
  i: Integer;
  strArr: TStringList;
  j: Integer;

  pJSONObj: ^TJSONObject;
begin
  strArr:= TStringList.Create;
  Split('.', Key, strArr);

  pJSONObj:= @STATICJSON;

  i:= 0;
  repeat
  begin
    if IsSimpleJsonValue(STATICJSON.GetValue(strArr[i])) then
    begin
      Exit;
    end;

    if STATICJSON.GetValue(strArr[i]) is TJSONObject then
    begin
      pJSONObj:= @(STATICJSON.GetValue(strArr[i]) as TJSONObject); -> error in this line
      if i + 1 = strArr.Count then
      begin

I store my values in a JSON object named STATICJSON until the program closes, and then I save it to file.

I am trying to add a field somewhere in STATICJSON. For example;

{
  "colors" :
    {
      "id": "F00",
      "color": "red",
      "details" : [
        {
         "name_en": "Red",
        },
        {
         "name_de": "Rot"
        }
      ]
   },
   {
      "id": "0F0",
      "color": "green",
      "details" : [
        {
         "name_en": "Green",
        },
        {
         "name_de": "Grün"
        }
      ]
   }
 }

If I want to add a code field in every details of the first object in the colors array:

obj.addField('colors.0.details.X', 'code');

What I expected :

{
  "colors" :
    {
      "id": "F00",
      "color": "red",
      "details" : [
        {
         "name_en": "Red",
         "code": ""
        },
        {
         "name_de": "Rot",
         "code": ""
        }
      ]
   },
   {
      "id": "0F0",
      "color": "green",
      "details" : [
        {
         "name_en": "Green",
        },
        {
         "name_de": "Grün"
        }
      ]
   }
 }

So I am trying to go to the details array of the first color and addField using a pointer.

I was using:

STATICJSON:= STATICJSON.GetValue(strArr[i]) as TJSONObject;

and it works well, but STATICJSON content is changed to this:

"details" : [
            {
             "name_en": "Red",
             "code": ""
            },
            {
             "name_de": "Rot",
             "code": ""
            }
          ]

So I lost all other JSON content. This is why I want to use a pointer. But I'm getting an error on this line:

pJSONObj:= @(STATICJSON.GetValue(strArr[i]) as TJSONObject);

Variable required

UPDATE :

procedure TJSONAdapter.addField(Key, Field: String);
var
  i: Integer;
  strArr: TStringList;
  j: Integer;

begin

  strArr:= TStringList.Create;
  Split('.', Key, strArr);

  i:= 0;
  repeat
  begin
    if IsSimpleJsonValue(STATICJSON.GetValue(strArr[i])) then
    begin
      Exit;
    end;

    if STATICJSON.GetValue(strArr[i]) is TJSONObject then
    begin
      STATICJSON:= STATICJSON.GetValue(strArr[i]) as TJSONObject;
      if i + 1 = strArr.Count then
      begin
        STATICJSON.AddPair(TJSONPair.Create(Field, ''));
        Exit;
      end;
    end;

    if STATICJSON.GetValue(strArr[i]) is TJSONArray then
    begin
      if strArr[i + 1] = 'X' then
      begin
        for j := 0 to (STATICJSON.GetValue(strArr[i]) as TJSONArray).Size -1 do
        begin
          ((STATICJSON.GetValue(strArr[i]) as TJSONArray).Get(j) as TJSONObject).AddPair(TJSONPair.Create(Field, ''));
        end;
        Exit;
      end;

      STATICJSON:= (STATICJSON.GetValue(strArr[i]) as TJSONArray).Get(StrToInt(strArr[i+1])) as TJSONObject;
      i:= i+1;

    end;
    Inc(i);
  end;
  until i = strArr.Count;

  strArr.Free;
end;

It works, but because of these lines:

STATICJSON:= STATICJSON.GetValue(strArr[i]) as TJSONObject;

STATICJSON:= (STATICJSON.GetValue(strArr[i]) as TJSONArray).Get(StrToInt(strArr[i+1])) as TJSONObject;

I lost the main JSON content, so I don't want to assign STATICJSON, i just want to assign its address to a pointer to not lose its content.


Solution

  • Delphi objects are reference types. TJSONObject is already a pointer, so there is no need to use ^TJSONObject.

    Try something more like this:

    var
      STATICJSON: TJSONObject = nil;
    
    procedure TJSONAdapter.addField(Key, Field: String);
    var
      I, J, Index: Integer;
      strArr: TStringList;
      pJSONVal: TJSONValue;
    begin
      strArr := TStringList.Create;
      try
        Split('.', Key, strArr);
        if STATICJSON = nil then begin
          STATICJSON := TJSONObject.Create;
        end;
        pJSONVal := STATICJSON;
        For I := 0 to strArr.Count-1 then
        begin
          if TryStrToInt(strArr[I], Index) then
          begin
            if not (pJSONVal is TJSONArray) then Exit; 
            pJSONVal := TJSONArray(pJSONVal).Get(Index);
          end
          else if strArr[I] = '*' then
          begin
            if I <> (strArr.Count-1) then Exit;
            if not (pJSONVal is TJSONArray) then Exit; 
            with TJSONArray(pJSONVal) do
            begin
              For J := 0 to Count-1 do
              begin
                pJSONVal := Get(Index);
                if pJSONVal is TJSONObject then
                  TJSONObject(pJSONVal).AddPair(Field, '');
              end;
            end;
          end
          else if pJSONVal is TJSONObject then
          begin
            pJSONVal := TJSONObject(pJSONVal).Get(strArr[I]);
            if pJSONVal = nil then Exit;
          end
          else Exit;
        end;
        if pJSONVal is TJSONObject then
          TJSONObject(pJSONVal).AddPair(Field, '');
      finally
        strArr.Free;
      end; 
    end;
    

    obj.addField('colors.0.details.*', 'code');