Nevermore
Nevermore

Reputation: 1743

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.

Upvotes: 1

Views: 679

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598174

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');

Upvotes: 1

Related Questions