Ошибка при освобождении корневого объекта JSON в Delphi 10.2: причины и решения
При работе с JSON в Delphi 10.2 и более поздних версиях, разработчики часто сталкиваются с проблемой освобождения памяти при наличии дублирующихся объектов внутри структуры JSON. Рассмотрим эту проблему и предложим несколько решений.
Проблема дублирования объектов в JSON
Основная проблема возникает, когда один и тот же объект TJsonObject добавляется в несколько мест JSON-структуры. При попытке освободить корневой объект происходит двойное освобождение памяти, что приводит к ошибке.
procedure TForm1.Button1Click(Sender: TObject);
var
aJsonObj, aJsonObj2, aJsonObj3: TJsonObject;
aJsonArr: TJsonArray;
begin
aJsonObj := TJsonObject.Create;
aJsonObj2 := TJsonObject.Create;
aJsonObj3 := TJsonObject.Create;
aJsonArr := TJsonArray.Create;
aJsonObj2.AddPair('aa','aa');
aJsonObj3.AddPair('bbb','bbb');
aJsonArr.Add(aJsonObj2);
aJsonArr.Add(aJsonObj3);
aJsonObj.AddPair('arr', aJsonArr);
aJsonObj.AddPair('a', aJsonObj2); // Тот же объект, что и в массиве
aJsonObj.AddPair('b', aJsonObj3); // Тот же объект, что и в массиве
ShowMessage(aJsonObj.ToJSON);
FreeAndNil(aJsonObj); // Здесь возникнет ошибка
end;
Решение 1: Использование свойства Owned
Все потомки TJSONAncestor имеют свойство Owned, которое определяет, должен ли контейнер освобождать экземпляр. Установив Owned := False для объектов, которыми вы хотите управлять вручную, можно избежать двойного освобождения.
procedure TForm1.Button1Click(Sender: TObject);
var
aJsonObj, aJsonObj2, aJsonObj3: TJsonObject;
aJsonArr: TJsonArray;
begin
aJsonObj := TJsonObject.Create;
aJsonObj2 := TJsonObject.Create;
aJsonObj2.Owned := False; // Контейнер не будет освобождать этот объект
aJsonObj3 := TJsonObject.Create;
aJsonObj3.Owned := False; // Контейнер не будет освобождать этот объект
aJsonArr := TJsonArray.Create;
aJsonObj2.AddPair('aa','aa');
aJsonObj3.AddPair('bbb','bbb');
aJsonArr.Add(aJsonObj2);
aJsonArr.Add(aJsonObj3);
aJsonObj.AddPair('arr', aJsonArr);
aJsonObj.AddPair('a', aJsonObj2);
aJsonObj.AddPair('b', aJsonObj3);
ShowMessage(aJsonObj.ToJSON);
// Освобождаем объекты вручную
FreeAndNil(aJsonObj);
FreeAndNil(aJsonObj2);
FreeAndNil(aJsonObj3);
end;
Решение 2: Создание копий объектов
Альтернативный подход - создание копий объектов с помощью метода Clone. Это гарантирует, что каждый объект в структуре JSON уникален.
procedure TForm1.Button1Click(Sender: TObject);
var
aJsonObj, aJsonObj2, aJsonObj2b, aJsonObj3, aJsonObj3b: TJsonObject;
aJsonArr: TJsonArray;
begin
aJsonObj := TJsonObject.Create;
aJsonObj2 := TJsonObject.Create;
aJsonObj3 := TJsonObject.Create;
aJsonArr := TJsonArray.Create;
aJsonObj2.AddPair('aa','aa');
aJsonObj3.AddPair('bbb','bbb');
aJsonArr.Add(aJsonObj2);
aJsonArr.Add(aJsonObj3);
// Создаем копии для добавления в корневой объект
aJsonObj2b := aJsonObj2.Clone as TJsonObject;
aJsonObj3b := aJsonObj3.Clone as TJsonObject;
aJsonObj.AddPair('arr', aJsonArr);
aJsonObj.AddPair('a', aJsonObj2b);
aJsonObj.AddPair('b', aJsonObj3b);
ShowMessage(aJsonObj.ToJSON);
FreeAndNil(aJsonObj);
end;
Решение 3: Рекурсивное освобождение JSON-структуры
Если вы предпочитаете полный контроль над освобождением памяти, можно реализовать собственную процедуру рекурсивного освобождения:
procedure SafeFreeJsonObject(var AJsonObject: TJsonObject);
var
I: Integer;
Item: TJsonValue;
begin
if not Assigned(AJsonObject) then Exit;
for I := AJsonObject.Count - 1 downto 0 do
begin
Item := AJsonObject.Pairs[I].JsonValue;
if Item is TJsonObject then
SafeFreeJsonObject(TJsonObject(Item))
else if Item is TJsonArray then
begin
for var J := TJsonArray(Item).Count - 1 downto 0 do
begin
if TJsonArray(Item).Items[J] is TJsonObject then
SafeFreeJsonObject(TJsonObject(TJsonArray(Item).Items[J]));
end;
Item.Free;
end
else
Item.Free;
end;
FreeAndNil(AJsonObject);
end;
Использование:
SafeFreeJsonObject(aJsonObj);
Вывод
При работе с JSON в Delphi важно учитывать следующие моменты:
1. Один объект не должен находиться в нескольких местах JSON-структуры, если вы не управляете его освобождением вручную
2. Свойство Owned позволяет контролировать процесс освобождения памяти
3. Метод Clone создает независимые копии объектов
4. При необходимости можно реализовать собственный механизм освобождения памяти
Выбор подхода зависит от конкретной задачи. Для простых случаев достаточно использовать свойство Owned, для более сложных структур может потребоваться создание копий или реализация собственного механизма управления памятью.
Ошибка при освобождении корневого объекта JSON в Delphi 10.2 возникает из-за двойного освобождения памяти при наличии дублирующихся объектов в структуре, что можно решить с помощью управления свойством Owned, создания копий объектов или реализации рекурс
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.