Search code examples
jsondelphisuperobjectsystem.json

Delphi XE7: How to change a JSON value using System.JSON (versus SuperObject)


I need to load a JSON file, change a value and then write it back to disk.

This is easy using SuperObject, but how do I do the same thing using the System.JSON unit?

const
  PathToX = 'AllCategories.Category[0].subCategory[0].products[0].views.view[0].x';

var
  JsonFilename: string;

  JO: ISuperObject; // from the SuperObject unit
  JV: TJsonValue;   // from the System.Json unit

begin
  JsonFilename := ExtractFilePath(Application.ExeName)+'product.json');

  // Using the SuperObject unit:
  JO := SO(TFile.ReadAllText(JsonFilename));

  WriteLn('The old value of "x" is ', JO[PathToX].AsString);
  WriteLn('Changing value of x to "123"');
  JO.S[PathToX] := '123';   // Set the value of "x"
  WriteLn('The new value of "x" is ', JO[PathToX].AsString);

  // Now trying to do the same thing using the System.Json unit:
  JV := TJSONObject.ParseJsonValue(TFile.ReadAllText(JsonFilename));

  WriteLn('The old value of "x" is ', JV.GetValue<string>(PathToX));
  WriteLn('Changing value of x to "123"');
// Question: What code should go here to set the value of "x" using System.JSON ??? 
  WriteLn('The new value of "x" is ', JV.GetValue<string>(PathToX));

There doesn't seem to be a "SetValue" equivalent to the "GetValue" method in System.JSON.


Solution

  • TJSONObject does support a path evaluator similar to SuperObject. So you will not have to manually drill into the JSON value tree one object at a time (though you certainly could if you wanted to).

    However, the System.JSON classes are actually NOT designed for modifying existing data (believe it or not)! They are designed for parsing data, and creating new data. All of the JSON classes that represent simple values (integers, boolean, strings) are read-only. Fortunately, the TJSONPair class allows a value to be replaced, so you will have to take advantage of that.

    Try something like this:

    uses
      ..., System.JSON;
    
    var
      JsonFilename: string;
      JV: TJSONValue;
      JO: TJSONObject;
      JoX: Integer;
      JoPair: TJSONPair;
    begin
      JsonFilename := ExtractFilePath(Application.ExeName) + 'product.json';
    
      JV := TJSONObject.ParseJSONValue(TFile.ReadAllText(JsonFilename));
      if JV = nil then raise Exception.Create('Cannot parse file: ' + JsonFilename);
      try
        JO := JV as TJSONObject;
    
        JoX := JO.GetValue<Integer>('AllCategories.Category[0].subCategory[0].products[0].colors.color[0].views.view[0].x');
        WriteLn('The old value of "x" is ', JoX);
    
        WriteLn('Changing value of "x" to "123"');
        JoPair := JO.GetValue<TJSONObject>('AllCategories.Category[0].subCategory[0].products[0].colors.color[0].views.view[0]').Get('x');
        JoPair.JsonValue.Free;
        JoPair.JsonValue := TJSONNumber.Create(123);
        WriteLn('The new value of "x" is ', JoPair.JsonValue.Value);
    
        SaveAsDialog.FileName := JsonFilename;
        if SaveAsDialog.Execute then TFile.WriteAllText(SaveAsDialog.FileName, JO.ToJSON);
      finally
        JV.Free;
      end;
    end;
    

    Alternatively:

    uses
      ..., System.JSON;
    
    var
      JsonFilename: string;
      JV: TJSONValue;
      JO: TJSONObject;
      JoX: TJSONPair;
    begin
      JsonFilename := ExtractFilePath(Application.ExeName) + 'product.json';
    
      JV := TJSONObject.ParseJSONValue(TFile.ReadAllText(JsonFilename));
      if JV = nil then raise Exception.Create('Cannot parse file: ' + JsonFilename);
      try
        JO := JV as TJSONObject;
    
        JoX := JO.GetValue<TJSONObject>('AllCategories.Category[0].subCategory[0].products[0].colors.color[0].views.view[0]').Get('x');
        WriteLn('The old value of "x" is ', JoX.JsonValue.Value);
    
        WriteLn('Changing value of "x" to "123"');
        JoX.JsonValue.Free;
        JoX.JsonValue := TJSONNumber.Create(123);
        WriteLn('The new value of "x" is ', JoX.JsonValue.Value);
    
        SaveAsDialog.FileName := JsonFilename;
        if SaveAsDialog.Execute then TFile.WriteAllText(SaveAsDialog.FileName, JO.ToJSON);
      finally
        JV.Free;
      end;
    end;