Delphi XE7: как изменить значение JSON с помощью System.JSON (по сравнению с SuperObject)

Мне нужно загрузить файл JSON, изменить значение и затем записать его обратно на диск.

Это легко с помощью SuperObject, но как мне сделать то же самое с модулем System.JSON?

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

Похоже, что "SetValue" не эквивалентен методу "GetValue" в System.JSON.

2 ответа

Решение

TJSONObject поддерживает оценку пути, аналогичную SuperObject. Таким образом, вам не придется вручную детализировать дерево значений JSON по одному объекту за раз (хотя вы, конечно, можете, если хотите).

Тем не менее System.JSON классы на самом деле НЕ предназначены для изменения существующих данных (хотите верьте, хотите нет)! Они предназначены для анализа данных и создания новых данных. Все классы JSON, представляющие простые значения (целые числа, логические значения, строки), доступны только для чтения. К счастью, TJSONPair Класс позволяет заменить значение, поэтому вам придется воспользоваться этим.

Попробуйте что-то вроде этого:

uses
  ..., System.JSON;

var
  JsonFilename: string;
  JO: TJSONObject;
  JoX: Integer;
  JoPair: TJSONPair;
begin
  JsonFilename := ExtractFilePath(Application.ExeName)+'product.json';

  JO := TJSONObject.ParseJSONValue(TFile.ReadAllText(JsonFilename)) as TJSONObject;
  if JO = nil then raise Exception.Create('Cannot parse file: ' + JsonFilename);
  try
    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
    JO.Free;
  end;
end;

В качестве альтернативы:

uses
  ..., System.JSON;

var
  JsonFilename: string;
  JO: TJSONObject;
  JoX: TJSONPair;
begin
  JsonFilename := ExtractFilePath(Application.ExeName)+'product.json';

  JO := TJSONObject.ParseJSONValue(TFile.ReadAllText(JsonFilename)) as TJSONObject;
  if JO = nil then raise Exception.Create('Cannot parse file: ' + JsonFilename);
  try
    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
    JO.Free;
  end;
end;

Ответ Реми не работает в Delphi 10.4 с нарушением прав доступа наJO.ToJSON. Оставляя без вниманияJoX.JsonValue.Freeисправляет это. Это работает:

      Var S := '{"Key1":"Foo","Key2":"Foo","Key3":"Foo"}';
Var JO := TJSONObject.ParseJSONValue(S) as TJSONObject;
Var JP := JO.Get('Key2');
// JP.JsonValue.Free; DO NOT DO THIS!
JP.JsonValue := TJSONString.Create('Bar');
S := JO.ToJSON;
JO.Free;

С теперь читает'{"Key1":"Foo","Key2":"Bar","Key3":"Foo"}', т.е. значение было заменено по назначению, а порядок пар остался неизменным. Согласно MadExcept, опускаяJP.JsonValue.Freeне оставил утечки памяти.

Другие вопросы по тегам