dwsJson Clone do not copy FItemIndex

Issue #179 closed
Douglas Cunha created an issue

I noticed that TdwsJSONObject.DoClone or TdwsJSONArray.DoClone do not copy FItemIndex field. As a result, I cannot get the item name from the Owner.Names property after I get a clone of my json object.

You can see my suggested fix bellow.

function TdwsJSONObject.DoClone: TdwsJSONValue;
var
   obj: TdwsJSONObject;
   member: TdwsJSONValue;
   i: Integer;
begin
   obj := TdwsJSONObject.Create;
   obj.SetCapacity(FCount);
   obj.FCount := FCount;
   obj.FItemIndex := FItemIndex; //I added this line
   for i := 0 to FCount-1 do begin
      obj.FItems[i].Name := FItems[i].Name;
      obj.FItems[i].Hash := FItems[i].Hash;
      member := FItems[i].Value.Clone;
      member.FOwner := obj;
      obj.FItems[i].Value := member;
   end;
   Result := obj;
end;

function TdwsJSONArray.DoClone: TdwsJSONValue;
var
   arr: TdwsJSONArray;
   elem: TdwsJSONValue;
   i: Integer;
begin
   arr := TdwsJSONArray.Create;
   arr.SetCapacity(FCount);
   arr.FCount := FCount;
   arr.FItemIndex := FItemIndex; //I added this line
   for i := 0 to FCount-1 do begin
      elem := FElements^[i].Clone;
      elem.FOwner := Self;
      arr.FElements^[i] := elem;
   end;
   Result := arr;
end;

Comments (6)

  1. Eric Grange repo owner

    Hi, I do not have an FItemIndex field here, are you using an older version ?

    Alternatively, would you have a minimal snippet that would reproduce the issue ? Thanks!

  2. Douglas Cunha reporter

    It looks like I do have an older or modified version here, anyway, that is the way I found to get the object name for an object in a hierarchy.
    For instance, if we have the follow JSON:

    {
        "objectName":
        {
            "objectItem1":
            {
                "prop": 1
            },
            "objectItem2":
            {
                "prop": 2
            }
        }
    }
    

    And I need to get the name of the object that contains “prop” I’d do that:

    procedure TesteSuperJSON.TesteTemp();
    const str = '{"objectName": {"objectItem1": { "prop": 1 }, "objectItem2": { "prop": 2 } } }';
    var
      json: TdwsJSONValue;
      item: TdwsJSONValue;
      i: Integer;
    begin
      json := TdwsJSONValue.ParseString(str);
    
      i := 0;
      for item in json.Items['objectName'] do
      begin
        Specify.That(item.ToString(), Should.&Not.Equal('')); //Here we have '{"prop":1}' we don't have the property name.
        //Without FItemIndex, the only way to get the name, is by calculating its Index.
        Specify.That(item.Owner.Names[i], Should.Equal('objectItem' + IntToStr(i+1)));
        //But, with FItemIndex I can easily get it this way and get rid of the aux "i" variable.
        Specify.That(item.Owner.Names[item.ItemIndex], Should.Equal('objectItem' + IntToStr(i+1)));
        Inc(i);
      end;
    end;
    

    As I could see, that is not a bug, since FItemIndex is no part of the official dwsjson library, but It could be an improvement. The best of scenarios would be if we have a property “TdwsJSONValue.Name” that gets the name of the object inside the hierarchy, like it:

    property Name: string read GetName;
    ....
    function TdwsJSONValue.GetName()
    begin
      Result := Owner.Names[ItemIndex];
    end;
    

    Regards!

  3. Eric Grange repo owner

    I am a bit uncertain of the use case…

    If the JSON is dynamically constructed/manipulated, maintaining the FItemIndex would be quite detrimental as the index is both heavily subject to change and meaningless for a JSON object (it’s an hashmap).
    When the JSON is static (such as after parsing), it is bound to be hierarchically traversed, so the parent’s name is either known, or only useful for debugging purposes, in which case a “slow” computation (without a maintained FItemIndex) would be enough.

    Is there another use case I am failing to see ?

  4. Log in to comment