Возможность работы с json в delphi без использования сторонних библиотек и компонентов впервые появилась в Delphi 2010. Именно тогда в составе исходников были обнаружены такие файлы как DBXJSON.pas, DBXJSONCommon.pas и DBXJSONReflect.pas, которые содержали необходимый минимум объектов и классов для парсинга и создания json-объектов в Delphi. Впоследствии, работа с json совершенствовалась, развивалась и библиотека delphi для json была перемещена в модули System.JSON.***.pas и, вместе с этим, получила новые возможности.
И вот теперь – в Delphi 10.3 Rio нам предлагается сразу два фреймворка для работы с JSON, каждый из которых по своему хорош и может быть использоваться в разработках под любые, поддерживаемые Delphi, операционные системы.
Delphi 10.3 Rio предоставляет две платформы (фреймворка, библиотеки) для обработки JSON:
По сути, именно с “JSON Objects Framework” начиналась вся работа с JSON в Delphi. Это достаточно простая в использовании библиотека, с помощью которой можно организовать обмен данными с каким-нибудь сервером, например, реализовать API онлайн-сервиса, или же сделать возможность сохранения настроек своей программы в json-формате и так далее.
В свою очередь, “Readers and Writers JSON Framework” – это логическое продолжение развития работы с JSON в Delphi. Как было указано выше, эта библиотека позволяет читать/писать json напрямую, не создавая при этом каких-либо промежуточных объектов. Логично предположить, что именно эту библиотеку стоит использовать при работе с “большими” JSON-данными, когда “JSON Objects Framework” не справляется с задачей парсинга json и выкидывает “Out of memory”. Более того, согласно официальной документации, “Readers and Writers JSON Framework” поддерживает работу с BSON.
Собственно, в этой статье я буду рассматривать работу библиотеки “JSON Objects Framework” для работы с json в delphi – посмотрю на удобство работы (с моей точки зрения), скорость обработки “большого” json и так далее. Затем, рассмотрю вторую библиотеку и, в итоге сравню обе библиотеки по нескольким критериям.
Как Вы, видимо, знаете, в основу JSON положены следующие понятия:
Например, вот такой JSON:
{ "host_id": "https:webdelphi.ru:443", "verified": true, "ascii_host_url": "https://webdelphi.ru/", "unicode_host_url": "https://webdelphi.ru/", "main_mirror": { "unicode_host_url": "https://webdelphi.ru/", "verified": false }, "host_data_status": "INDEXED", "host_display_name": "Блог WebDelphi.ru" }
Содержит один вложенный объект с именем main_mirror и пары host_id, verified, host_display_name и так далее.
А этот JSON содержит массив вложенных объектов (original_texts) и пары (count и quota_remainder). При этом массив содержит вложенный объект (в примере объект один, однако в реальности таких объектов может быть сколько угодно), в котором содержатся свои пары (id, content_snippet и date).
{ "original_texts": [ { "id": "explicit error message", "content_snippet": "explicit error message", "date": "2016-01-01T00:00:00,000+0300" } ], "count": 1, "quota_remainder": 1 }
Таким образом, JSON может быть какой угодно сложности – содержать или не содержать массивы и вложенные объекты, пары могут содержать значения boolean, null, числа или строки, имена пар могут несколько раз повторяться и так далее.
Для просмотра небольших по объему JSON-объектов можете воспользоваться приложением “JSON Viewer”. Скачать программу можно отсюда (ссылка для скачивания находится в конце статьи) или отсюда.
JSON Viewer покажет вам json в виде дерева. Например, последний представленный выше пример json в программе будет выглядеть вот так:
Теперь, освежив немного в памяти, что из себя представляет JSON, можно приступать к рассмотрению библиотек для работы с JSON в Delphi.
“JSON Objects Framework” поддерживает все типы JSON, которые в Delphi представлены соответствующими классами:
Все эти классы являются потомками абстрактного класса TJSONValue, который, в свою очередь, является потомком TJSONAncestor. В этом плане ничего не поменялось с момента выхода Delphi 2010.
Начинать работу по созданию нового JSON следует с создания нового объекта (TJSONObject).
Для примера, создадим JSON-объект, который будет содержать всего одну пару следующего вида “Привет: мир!”. То есть строка “Привет” – это будет имя пары, а строка “мир!” – её (пары) значения.
В Delphi такой JSON-объект создается элементарно:
var JsonObject: TJSONObject; begin JsonObject:=TJSONObject.Create; JsonObject.AddPair('Привет', 'мир!');
В итоге, JsonObject будет содержать следующий json:
{ "Привет": "мир!" }
Метод AddPair добавляет в JSON-объект новую пару и имеет следующее описание:
function AddPair(const Pair: TJSONPair): TJSONObject; overload; function AddPair(const Str: TJSONString; const Val: TJSONValue): TJSONObject; overload; function AddPair(const Str: string; const Val: TJSONValue): TJSONObject; overload; function AddPair(const Str: string; const Val: string): TJSONObject; overload;
Этих четырех реализаций метода AddPair вполне достаточно, чтобы создать любой по сложности JSON-объект. Например, создадим вот такой JSON:
{ "String": "Строка", "Integer": 255, "Boolean": true, "Double": 10.532, "Null": null }
Создать такой JSON достаточно просто:
Вариант №1. Последовательно добавить каждую новую пару в объект:
var JSON: TJSONObject; begin JSON:= TJSONObject.Create; //Добавляем в json строку JSON.AddPair('String','Строка'); //Добавляем в json число Integer JSON.AddPair('Integer',TJSONNumber.Create(255)); //Добавляем в json boolean JSON.AddPair('Boolean',TJSONBool.Create(True)); //Добавляем в json число Double JSON.AddPair('Double',TJSONNumber.Create(10.532)); //Добавляем в json Null JSON.AddPair('Null',TJSONNull.Create);
Вариант №2. Использовать результат функции AddPair и добавлять новые пары “в одну строку”:
JSON.AddPair('String','Строка') .AddPair('Integer',TJSONNumber.Create(255)) .AddPair('Boolean',TJSONBool.Create(True)) .AddPair('Double',TJSONNumber.Create(10.532)) .AddPair('Null',TJSONNull.Create);
Какой из вариантов вам больше нравится – тот и используйте. Аналогичным образом создаются и более сложные JSON-объекты – содержащие вложенные объекты, массивы и так далее.
Например, создадим json следующего содержания:
{ "kind":"tasks#task", "title":"Новая задача", "links": [ { "type":"href", "description":"desc", "link":"http://webdelphi.ru" } , { "type":"href2", "description":"desc", "link":"http://webdelphi.ru" } ] }
Такой json уже содержит вложенный массив из двух элементов, где каждый элемент – вложенных объект с тремя парами. В JSON Viewer этот json выглядит следующим образом:
Создадим такой JSON в Delphi, используя JSON Objects Framework:
var JSON: TJSONObject; JSONArray: TJSONArray; JSONNestedObject: TJSONObject; begin JSON:= TJSONObject.Create; try //Добавляем в json строки JSON.AddPair('kind','tasks#task') .AddPair('title','Новая задача'); //Создаем массив JSONArray:=TJSONArray.Create; {-- добавляем в массив первый объект --} // 1. заносим в массив пустой json-объект JSONArray.AddElement(TJSONObject.Create); //получаем ссылку на добавленный объект JSONNestedObject:=JSONArray.Items[pred(JSONArray.Count)] as TJSONObject; //заполняем объект данными JSONNestedObject.AddPair('type','href') .AddPair('description','desc') .AddPair('link','http://webdelphi.ru'); {-- добавляем в массив второй объект --} // 1. заносим в массив пустой json-объект JSONArray.AddElement(TJSONObject.Create); //получаем ссылку на добавленный объект JSONNestedObject:=JSONArray.Items[pred(JSONArray.Count)] as TJSONObject; //заполняем объект данными JSONNestedObject.AddPair('type','href2') .AddPair('description','desc') .AddPair('link','http://webdelphi.ru'); //записываем массив в json-объект JSON.AddPair('links', JSONArray);
Поверьте, мы сейчас создали именно такой json-объект, как представлено выше. Как видите, ничего особенно сложного в создании любых JSON-объектов нет. Главное, на что стоит обращать внимание при создании json в delphi – какая пара является родителем для других пар. Все JSON-объекты, которые мы сейчас создавали были “видны” и понятны только нам, так как мы не пытались их каким-любо образом отобразить, например, в Memo. Вместе с этим, JSON Objects Framework представляет сразу несколько методов для “печати” созданных JSON-объектов.
Для того, чтобы преобразовать созданный JSON-объект в строку у JSON Objects Framework имеются следующие методы:
function ToString: string; override; function ToJSON: string; function Format(Indentation: Integer = 4): string; overload; function ToBytes(const Data: TArray; Offset: Integer): Integer;override; procedure ToChars(Builder: TStringBuilder);override;
Метод ToBytes представляет JSON-объект в виде массива байтов. Этот метод удобно использовать, например, когда необходимо перекодировать полученную строку в какую-либо кодировку, например, ANSI или UTF-8. Этот метод используется в методе ToJSON.
Метод ToJSON представляет JSON-объект в виде строки в кодировке UTF-8. Так будет выглядеть JSON-объект из последнего примера:
{"kind":"tasks#task","title":"\u041D\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0430\u0447\u0430","links":[{"type":"href","description":"desc","link":"https:\/\/www.webdelphi.ru"},{"type":"href2","description":"desc","link":"http:\/\/webdelphi.ru"}]}
В свою очередь, метод ToString переводит Json-объект в UnicodeString и результат представления будет уже таким:
{"kind":"tasks#task","title":"Новая задача","links":[{"type":"href","description":"desc","link":"https:\/\/www.webdelphi.ru"},{"type":"href2","description":"desc","link":"http:\/\/webdelphi.ru"}]}
Как видите, русские буквы будут представлены “как есть”.
Метод ToChars использует объект TStringBuilder для представления JSON-объекта. Используется метод следующим образом:
var SB: TStringBuilder; SB:=TStringBuilder.Create; JSON.ToChars(SB); SB.ToString
Соответственно, если после этого не проводить с TStringBuilder никаких манипуляций, то результатом выполнения метода ToChars будет всё та же строка, что и при использовании метода ToString:
{"kind":"tasks#task","title":"Новая задача","links":[{"type":"href","description":"desc","link":"https:\/\/www.webdelphi.ru"},{"type":"href2","description":"desc","link":"http:\/\/webdelphi.ru"}]}
Метод Format применяется в том случае, если JSON-объект необходимо представить в человеко-понятном виде (с отступами и форматированием). При этом, в качестве параметра функции Format можно указать количество отступов для дочерних объектов (по умолчанию значение равно 4). Результатом работы Format будет следующее представление JSON-объекта:
{ "kind": "tasks#task", "title": "Новая задача", "links": [ { "type": "href", "description": "desc", "link": "https:\/\/www.webdelphi.ru" }, { "type": "href2", "description": "desc", "link": "http:\/\/webdelphi.ru" } ] }
Также следует обратить внимание на то, что при представлении JSON-объекта в виде строки delphi используется экранирование таких символов как ‘/’, ‘”‘, ”’ и так далее.
Таким образом, любой JSON-объект в “JSON Objects Framework” можно представлять: в виде массива байтов, в виде строки UTF-8, в виде строки Unicode, в любой кодировке, используя массив байтов или объект TStringBuilder, в человеко-читаемом виде, настраивая при этом количество отступов дочерних элементов.
С созданием и представлением JSON-объектов в Delphi разобрались. Следующая задача – научиться разбирать JSON любой сложности в Delphi.
Разбор json в delphi при использовании “JSON Objects Framework” следует начинать также как и при создании нового json-объекта, то есть создать объект TJSONObject. Далее, в зависимости от того, что нам необходимо получить в результате парсинга JSON мы можем:
Как я уже сказал выше, парсинг начинается с создания (получения) объекта TJSONObject.
Сделать это можно следующими способами:
во-первых, воспользоваться классовым методом ParseJSONValue, который в Delphi имеет следующее описание:
class function ParseJSONValue(const Data: PByte; const Offset: Integer; const ALength: Integer; Options: TJSONParseOptions): TJSONValue; overload; static; class function ParseJSONValue(const Data: TArray; const Offset: Integer; IsUTF8: Boolean = True): TJSONValue; overload; inline; static; class function ParseJSONValue(const Data: TArray; const Offset: Integer; Options: TJSONParseOptions): TJSONValue; overload; inline; static; class function ParseJSONValue(const Data: TArray; const Offset: Integer; const ALength: Integer; IsUTF8: Boolean = True): TJSONValue; overload; inline; static; class function ParseJSONValue(const Data: TArray; const Offset: Integer; const ALength: Integer; Options: TJSONParseOptions): TJSONValue; overload; inline; static; class function ParseJSONValue(const Data: string; UseBool: Boolean = False; RaiseExc: Boolean = False): TJSONValue; overload; static; class function ParseJSONValue(const Data: UTF8String; UseBool: Boolean = False; RaiseExc: Boolean = False): TJSONValue; overload; static;
Параметры метода следующие:
Например, используя метод ParseJSONValue, можно распарсить json и получить TJSONObject можно следующим образом:
var JSON: TJSONObject; begin JSON:=TJSONObject.ParseJSONValue('{"firstName": "Петров","lastName": "Пётр"}', False, True) as TJSONObject; {работаем с полученным TJSONObject}
во-вторых, распарсить json в delphi можно, используя метод Parse:
function Parse(const Data: TArray; const Pos: Integer; UseBool: Boolean = False): Integer; overload; function Parse(const Data: TArray; const Pos: Integer; const Count: Integer; UseBool: Boolean = False): Integer; overload;
Параметры метода, аналогичны параметрам ParseJSONValue, то есть:
Метод Parse можно использовать, например, так:
const cJsonStr = '{'+ '"firstName": "Петров",'+ '"lastName": "Пётр",'+ '"address": {'+ ' "streetAddress": "ул. Дельфийская, 101, кв.101",'+ ' "city": "Дельфийск",'+ ' "postalCode": "100301"'+ '},'+ '"phoneNumbers": ['+ ' "812 123-1234",'+ ' "916 123-4567"'+ ']'+ '}'; var JSON: TJSONObject; JSONArray: TJSONArray; JSONNestedObject: TJSONObject; begin JSON:=TJSONObject.Create; JSON.Parse(TEncoding.UTF8.GetBytes(cJsonStr),0);
В приведенном примере мы использовали TEncoding.UTF8 для получения массива байтов в кодировке UTF-8. Если бы мы этого не сделали, то в результате вызова метода Parse мы получили бы исключение. Пример ошибочного вызова метода Parse (в примере используется тот же json, то и выше):
JSON:=TJSONObject.Create; JSON.Parse(BytesOf(cJsonStr),0);
В результате, получим вот такое исключение:
UTF8: An unexpected continuation byte in 2-byte UTF8. Path ”, line 1, position 17 (offset 16)
Теперь, получив в свое распоряжение TJSONObject, можно приступать к получению из него необходимых на данных. Рассмотрим несколько примеров того, как можно как парсить json в Delphi 10.3 Rio, используя JSON Objects Framework.
Например, нам необходимо получить имя и фамилию человека из вот такого объекта JSON:
{ "firstName": "Петров", "lastName": "Пётр", "address": { "streetAddress": "ул. Дельфийская, 101, кв.101", "city": "Дельфийск", "postalCode": "100301" }, "phoneNumbers": [ "812 123-1234", "916 123-4567" ] }
Сделать это можно следующими способами:
1. Получить доступ к парам json-объекта, используя свойство Values у TJSONObject:
const cJsonStr = '{'+ '"firstName": "Петров",'+ '"lastName": "Пётр",'+ '"address": {'+ ' "streetAddress": "ул. Дельфийская, 101, кв.101",'+ ' "city": "Дельфийск",'+ ' "postalCode": "100301"'+ '},'+ '"phoneNumbers": ['+ ' "812 123-1234",'+ ' "916 123-4567"'+ ']'+ '}'; var JSON: TJSONObject; begin JSON:=TJSONObject.ParseJSONValue(cJsonStr,False, True) as TJSONObject; try ShowMessage(JSON.Values['firstName'].Value); ShowMessage(JSON.Values['lastName'].Value); finally JSON.Free; end; end;
здесь мы указали в свойстве Value имя пары значение которой необходимо получить. Value возвращает объект TJSONValue свойство Value которого содержит значение пары. Аналогичным образом работает и метод TJSONObject GetValue:
function GetValue(const Name: string): TJSONValue; overload;
Поиск чувствителен к регистру и возвращает первую пару со строковой частью (именем), совпадающей с аргументом Name.
2. Использовать метод FindValue
В отличие от свойства Value этот метод позволяет указывать путь к определенному значению в json-объекте.
function FindValue(const APath: string): TJSONValue;
Если значение не будет найдено, то метод вернет значение nil.
Применительно к нашей задаче (чтение имени и фамилии из json) использование FindValue будет точно таким же, как и в предыдущем способе использование свойства Value, то есть код Delphi будет вот таким:
JSON:=TJSONObject.Create; try JSON.Parse(TEncoding.UTF8.GetBytes(cJsonStr),0); ShowMessage(JSON.FindValue('firstName').Value); ShowMessage(JSON.FindValue('lastName').Value); finally JSON.Free; end;
Однако, здесь мы не раскрыли всех преимуществ этой функции. Например, с помощью FindValue мы можем получить доступ к значениям, содержащимся во вложенных объектах JSON. В нашем тестовом JSON-объекте есть вложенный json-объект, содержащий почтовый адрес. Прочитать, например, название улицы не составит никакого труда:
JSON.FindValue('address.city').Value;
Здесь мы указали уже не имя пары JSON, а путь – прочитали значение пары “city” из вложенного json “address”.
3. Использовать перечислитель всех пар JSON-объекта
Этот способ удобно использовать, например, когда необходимо получить вообще все данные из json, а не только отдельные значения. Или же в том случае, если какой-либо пары в json-объекте может не быть в зависимости от наличия той или иной информации на сервере (такое, кстати, вполне возможно при работе с API онлайн-сервисов).
Вот как может выглядеть чтение строк из json с использованием перечислителя (TJSONObject.TEnumerator):
var JSON: TJSONObject; JSONEnum: TJSONObject.TEnumerator; begin //создаем JSON-объект и парсим json JSON:=TJSONObject.Create; JSON.Parse(TEncoding.UTF8.GetBytes(cJsonStr),0); try //получаем доступ к перечислителю JSONEnum:=JSON.GetEnumerator; //пока есть не перечисленные пары в json - переходим на следующую пару while JSONEnum.MoveNext do //проверяем имя json-пары и, если находим подходящее - выводим сообщение if SameText(JSONEnum.Current.JsonString.Value, 'firstName') or SameText(JSONEnum.Current.JsonString.Value, 'lastName') then ShowMessage(JSONEnum.Current.JsonValue.Value);
Таким образом, прочитать строку из json в delphi можно, как минимум, тремя способами:
Ну и, напоследок, следующий код вернет значение пары или сообщение об ошибке, если пара отсутствует в json-объекте:
var S: string; if JSON.TryGetValue('lastName', S) then ShowMessage(S) else ShowMessage('Пара lastName не найдена')
Отдельный пример работы с JSON в Delphi – получение данных из массива json. В приведенном выше примере JSON массив содержит номера телефонов:
{ .... "phoneNumbers": [ "812 123-1234", "916 123-4567" ] }
Как в этом случае перечислить все элементы массива json, используя возможности “JSON Objects Framework”?
Опять же, решение этой задачи в Delphi подразумевает использование нескольких вариантов.
1.Использование свойств TJSONArray для перечисления всех элементов массива json
Объект TJSONObject, в числе прочих, содержит следующие свойства:
Используя эти свойства, перечислить все элементы массива из примера можно следующим образом:
JSON:=TJSONObject.Create; JSON.Parse(TEncoding.UTF8.GetBytes(cJsonStr),0); try //получаем доступ к массиву json JSONArray:=JSON.Values['phoneNumbers'].AsType; //перечисляем все элементы массива for I := 0 to Pred(JSONArray.Count) do ShowMessage(JSONArray.Items[i].Value)
Здесь строит обратить внимание на последнюю строку:
ShowMessage(JSONArray.Items[i].Value)
так как мы знаем, что массив json содержит только строки, то мы смогли сразу использовать свойство Value и вывести значение элемента массива пользователю. Но массив json может содержать не только отдельные строки, но и, например, объекты json. В этом случае парсинг массива json в delphi немного усложняется.
2. Использование перечислителей массивов json
У объекта TJSONArray имеется свой тип перечислителя – TJSONArray.TEnumerator. работает он аналогично перечислителю объекта TJSONObject, который мы рассматривали выше. Например, вот так мы можем перечислить все номера телефонов из json, представленного выше:
var JSON: TJSONObject; JSONArray: TJSONArray; JsonArrEnum: TJSONArray.TEnumerator; begin JSON:=TJSONObject.Create; JSON.Parse(TEncoding.UTF8.GetBytes(cJsonStr),0); try //получаем доступ к массиву json JSONArray:=JSON.Values['phoneNumbers'].AsType; //получаем перечислитель массива JsonArrEnum:=JSONArray.GetEnumerator; //перечисляем все элементы массива json while JsonArrEnum.MoveNext do ShowMessage(JSONArrEnum.Current.Value);
Таким образом, используя представленные выше варианты работы с массивом json в “JSON Objects Framework” можно получать любую интересующую вас информацию.
Рассмотрим более интересную задачу, которую можно решать в delphi при использовании с json – работу с вложенными объектами.
Попробуем разобрать вот такой JSON:
{ "symbol":"AAPL", "stock_exchange_short":"NASDAQ", "timezone_name":"America/New_York", "intraday": { "2018-10-1915:59:00": { "open":"23", "low":"4" } , "2018-10-1915:58:00": { "open":"25", "low":"21" } } }
Дерево этого JSON в программе JSON Viewer будет выглядеть следующим образом:
Обратите внимание на пару “interday” – она является объектом json и, при этом, каждая пара этого объекта в качестве значения содержит вложенный json-объект. Что касается пар в начале объекта – symbol и прочих, то они уже для нас не представляют особого интереса – примеры по работе с ними представлены выше, а вот как разобрать intraday – рассмотрим более детально.
Итак, каждая пара в interday имеет свое уникальное имя (дату). Вложенный JSON-объект, в свою очередь, содержит две пары – open и low, которые и требуется прочитать.
Алгоритм парсинга json может быть следующим:
Реализация этого алгоритма парсинга json, содержащего вложенные объекты может быть следующей:
cJsonStr = '{' + ' "symbol":"AAPL",' + ' "stock_exchange_short":"NASDAQ",' + ' "timezone_name":"America/New_York",' + ' "intraday":' + ' { ' + ' "2018-10-1915:59:00":' + ' {' + ' "open":"23",' + ' "low":"4"' + ' }' + ',' + ' "2018-10-1915:58:00":' + ' {' + ' "open":"25",' + ' "low":"21" ' + ' }' + ' }' + '}'; var JSONValue: TJSONValue; JsonNestedObject: TJSONObject; quote, low, open: string; begin JSONValue := TJSONObject.ParseJSONValue(cJsonStr).AsType; JsonNestedObject := JSONValue.FindValue('intraday').AsType; with JsonNestedObject.GetEnumerator do // цикл будет выполняться до тех пор, пока в JSON-объекте есть хотя бы одна не пройденная пара while MoveNext do begin // получаем название вложенного объекта (для первого прохода в цикле - это 2018-10-1915:59:00). quote := Current.JsonString.Value; // считываем значение пары low low := (Current.JSONValue as TJSONObject).Values['low'].Value; // считываем значение пары open open := (Current.JSONValue as TJSONObject).Values['open'].Value; // выводим полученные значения в Memo Memo1.Lines.Add(Format('%s, %s, %s', [quote, low, open])) end; end;
Как видите, несмотря на кажущуюся сложность, разбор json в delphi оказался не таким уж и сложным, если воспользоваться знаниями о возможностях “JSON Objects Framework”.
В JSON родительский объект владеет любым из содержащихся в нем значений, если для свойства Owned не установлено значение False. В этом случае уничтожение объекта JSON пропускает каждый элемент, для которого установлен флаг False. Эта возможность позволяет объединять различные объекты в более крупные, сохраняя при этом право владения объектами. По умолчанию свойство имеет значение True, что означает, что все содержащиеся в нем экземпляры принадлежат их родителям.
Чтобы оценить производительность “JSON Objects Framework” я решил провести следующие тесты:
На чем тестировалась работа:
В качестве первого теста я создавал вот такой JSON:
{ "Pair":"Value", "Pair":"Value", ... }
Количество пар в json-объекте: 500 000
Время выполнения операции засекалось от момента создания экземпляра TJSONObject:
JSON := TJSONObject.Create;
До полного уничтожения объекта:
FreeAndNil(JSON);
Код теста:
var JSON: TJSONObject; PName, PValue: string; t1: TStopwatch; Count, Limit: integer; Str: TStringStream; begin Count :=10;//количество повторений теста Limit :=500000;//количество пар в json PName :='Pair'; PValue:='Value'; StringGrid1.RowCount:=Count+1; //настраиваем количество строк таблицы for var I := 1 to Count do begin // засекаем всё время - от создания объекта до его уничтожения t1 := TStopwatch.StartNew; JSON := TJSONObject.Create; try for var J := 0 to Pred(Limit) do JSON.AddPair(PName, PValue); finally FreeAndNil(JSON); end; t1.Stop; //выводим результаты теста StringGrid1.Cells[0,i]:=I.ToString; StringGrid1.Cells[1,i]:=t1.ElapsedMilliseconds.ToString; end; end;
Результаты тестирования:
Для второго теста я создавал JSON следующего содержания:
{ "Array": [ "TestValue", "TestValue" ] , "Array": [ "TestValue", "TestValue" ] }
Код теста:
var JSON: TJSONObject; JSONArray: TJSONArray; t1: TStopwatch; ArrayCount, ArrayLimit: integer; TestCount: integer; begin ArrayCount := 10; ArrayLimit := 500000; TestCount:=10; StringGrid2.RowCount:=TestCount+1; for var I := 1 to TestCount do begin // засекаем всё время - от создания объекта до его уничтожения t1 := TStopwatch.StartNew; JSON := TJSONObject.Create; try for var j := 1 to ArrayCount do begin JSONArray:=TJSONArray.Create; for var k := 1 to ArrayLimit do JSONArray.AddElement(TJSONString.Create('TestValue')); JSON.AddPair('Array',JSONArray); end; finally FreeAndNil(JSON); end; t1.Stop; StringGrid2.Cells[0,i]:=I.ToString; StringGrid2.Cells[1,i]:=t1.ElapsedMilliseconds.ToString; end; end;
Результаты тестирования:
В целом, учитывая, что тестирование проводилось на не самом слабом ПК, можно сказать, что производительность “JSON Objects Framework” по созданию новых объектов JSON в Delphi достаточно хорошая.
Теперь попробуем распарсить полученные JSON.
Оба JSON-объекта, созданные в предыдущем тесте были сохранены в файл. В этой части статьи будем их парсить.
Разбор JSON, содержащего пары значений:
Код теста:
var S: TStringStream; JSON: TJSONObject; t_parse: TStopwatch; Bytes: TArray; begin //считываем файл в TStringStream S := TStringStream.Create('', TEncoding.UTF8); try S.LoadFromFile('SimpleJson.txt'); Bytes := S.Bytes; finally FreeAndNil(S); end; StringGrid2.RowCount:=11; for var I := 1 to 10 do begin //засекаем время от момента создания TJSONObject t_parse := TStopwatch.StartNew; JSON := TJSONObject.Create; try //парсим JSON JSON.Parse(Bytes, 0); finally FreeAndNil(JSON); end; //останавливаем таймер после уничтожения TJSONObject t_parse.Stop; //выводим результаты теста в TStringGrid StringGrid2.Cells[0, I] := I.ToString; StringGrid2.Cells[1, I] := t_parse.ElapsedMilliseconds.ToString; end; end;
Результаты парсинга JSON:
Парсинг JSON, содержащего массивы значений. Здесь код теста идентичный предыдущему (меняется только название файла), поэтому приводить я его не буду, а сразу покажу результаты теста.
Как видно из представленных результатов, парсинг JSON происходит примерно в 1,5 раза дольше, чем его (JSON) создание, но, в принципе, тоже приемлемо для небольших по объему JSON-объектов.
Однако, как я говорил ранее, в начале статьи, бывают случаи, когда “JSON Objects Framework” не справляется с задачей парсинга. Примером JSON, который нет возможности разобрать может служить набор открытых данных одного из органов государственной власти РФ. Архив с данными можно скачать здесь. Если по каким-либо причинам вы не можете скачать этот файл, то здесь лежит его копия. Файл размером порядка 544 Мб. Очевидно, что в этом случае необходимо использовать второй фреймворк для работы с JSON в Delphi – Readers and Writers JSON Framework. И именно о нем мы поговорим с вами в следующей статье блога.
Итак, я постарался подробно рассмотреть возможности “JSON Objects Framework” для работы с json в delphi. Библиотека, в настоящий момент, представляет собой достаточно удобный инструмент для работы с небольшими и средними по размеру JSON-объектами, позволяет создавать любые по сложности объекты, работать с массивами json, вложенными объектами, позволяет учитывать наличие BOM в строке, содержащей json и так далее. Более того, с момента своего первого выхода, библиотека стала, на мой взгляд, более удобной и производительной.
Таким образом, я бы рекомендовал “JSON Objects Framework” в качестве отправной точки для начала знакомства работы с json в delphi, а также для использования этой библиотеки для организации обмена данными при реализации какого-либо API онлайн-сервиса в Delphi.
Автор: Андрей Шкрыль Название: Разработка клиент-серверных приложений в Delphi Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД. | ||
Автор: Антон Григорьев Название: О чем не пишут в книгах по Delphi Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др. |