Работа с форматами XML и JSON в FastScript

21.04.2025

В настоящее время форматы XML и JSON стали основными стандартами для хранения данных и обмена информацией между компьютерными системами. По просьбам пользователей в FastScript добавлены классы для работы с XML и JSON. В этой статье мы подробнее рассмотрим, как работать с этими классами, какие в них есть свойства и методы, а также построим отчеты из кода со скриптами.

 


 

Формат XML

Для работы с XML в скриптах применяются 2 класса. Данные классы ориентированы на максимальное быстродействие и малое потребление памяти.

Классы для работы с XML

 

TfrXMLDocument – класс, инкапсулирующий функциональность документа XML. В нём доступны следующие свойства и методы.

Свойства и методы класса Описание
procedure SaveToStream(Stream: TStream); Сохраняет XML в переданном потоке.
procedure LoadFromStream(Stream: TStream; AllowPartialLoading: Boolean = False); Загружает XML из переданного потока.
procedure SaveToFile(const FileName: string); Сохраняет XML в файл с указанным именем.
procedure LoadFromFile(const FileName: string); Загружает XML из файла с указанным именем.
procedure Clear; Удаляет у дерева все узлы XML, кроме корневого. Содержимое узла при этом НЕ очищается.
property Root: TfrXMLItem; Позволяет получить доступ к корневому элементу дерева.
property AsText: string; Позволяет как получить, так и задать XML в виде строки (например, используя это свойство, можно передать XML в скрипт отчета с помощью переменной отчета).
property AutoIndent: Boolean; Определяет, как будет генерироваться выходной XML: в виде одной строки или в виде текста с отступами.

 

TfrXMLNode – класс, который инкапсулирует в себе свойства узла XML документа.

Создать объект данного типа напрямую нельзя. Он создается путем вызова метода Add() у элемента, к которому необходимо добавить дочерний элемент. Рассмотрим подробнее свойства и методы класса TfrXMLNode.

Свойства и методы класса Описание
function Add(AName:string):TfrXMLItem; Создает дочерний TfrXMLItem с заданным именем и возвращает его.
procedure Clear; Очищает список дочерних элементов.
procedure InsertItem(Index:integer; AItem: TfrXMLItem); Вставляет дочерний элемент в указанную позицию. Элемент может принадлежать другому документу.
function Find(AName:string):Integer; Ищет в дочерних элементах TfrXMLItem с указанным именем и возвращает его. Если элемент не найден, то возвращается -1. Если элементов с заданным именем несколько, то возвращается первый из них.
function FindItem(AName:string):TfrXMLItem; Ищет в дочерних элементах TfrXMLItem с указанным именем и возвращает его. Если элемент не найден, то создается и возвращается новый элемент с указанным именем. Если элементов с заданным именем несколько, то возвращается первый из них.
function Root: TfrXMLItem; Возвращает корневой элемент документа.
property Count:Integer; Возвращает количество дочерних узлов элемента.
property Items[AIndex:Integer]: TfrXMLItem; Возвращает дочерний элемент по его индексу.
property Prop[AName:string]:string; Возвращает или задает значение атрибута узла с заданным именем.
property Name: string; Имя тэга элемента.
property Parent: TfrXMLItem; Имя родительского элемента. У Root равно nil.
property Text:string; Строка, содержащая список параметров узла в виде Name1=”Value1” Name2=”Value2”… Специальные символы в этой строке экранированы.
procedure Delete(AIndex: Integer); Удаляет дочерний элемент с индексом AIndex.
procedure DeleteProp(const APropName: string); Удаляет атрибут узла с заданным именем.
property Value: string; Текст, который находится между открывающим и закрывающим тэгом элемента.
function PropExists(APropName:string):Boolean; Помогает определить имеется ли у элемента атрибут с указанным именем.
function IndexOf(AItem: TfrXMLItem):Integer; Возвращает индекс переданного элемента.
function GetPropNames(ANames:TStrings); Заполняет переданный список строк именами атрибутов TfrXMLItem.

 

Давайте попробуем построить отчет с использованием классов для работы с XML. Для примера отобразим в отчете данные из файла «country.xml», но при этом не будем пользоваться TClientDataSet, а воспользуемся прямым доступом к файлу XML. Создаём новый отчет, переходим в режим редактирования, кладём на отчет MasterData. А сверху добавляем несколько Memo для отображения данных. Также создадим событие OnBeforePrint у объекта MasterData.

Итоговый код скрипта отчета будет выглядеть так:

var doc: TfrXMLDocument;
 item: TfrXMLItem;
 IIndex: Integer;
 
procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
var cur: TfrXMLItem;
 S: string;
begin
 cur := item[IIndex];
 mmNum.Text := cur.Prop['Code'];
 mmName.Text := cur.Prop['Name'];
 mmCapital.Text := cur.Prop['Capital'];
 mmArea.Text := cur.Prop['Area'];
 mmPopulation.Text := cur.Prop['Population'];
 mmContinent.Text := cur.Prop['Continent'];
 Inc(IIndex);
end;
 
begin
 Doc := TfrXMLDocument.Create;
 Doc.LoadFromFile('..\..\Data\country.xml');
 item := Doc.Root[1];
 IIndex := 0;
 MasterData1.RowCount := item.count;
end.


Запустим отчет на выполнение. Для удобства восприятия мы также добавили заголовки столбцов.

Готовый отчет

 


 

Формат JSON

Из формата XML плавно перейдём в JSON. Для работы с ним в FastReport также применяются 2 класса – это TfrJSON и TfrJSONArray. В данном случае эти классы просто обертка вокруг классов из System.JSON в Delphi платформе или аналогичных классов в Lazarus.

Классы для работы с JSON

 

TfrJSONObject – класс, который инкапсулирует в себе функции для работы с объектами JSON. 

Свойства и методы класса Описание
function Create(const JSONstring: string); Создает объект JSON и заполняет его из переданной строки.
function IsValid:Boolean; Возвращает True, если объект содержит валидный JSON.
procedure LodFromFile(const AFilName:string); Загружает данные из указанного файла. Если в объекте содержались данные, то они пропадут.
procedure LoadFromtStream(const AStream:TStream); Загружает данные из переданного потока. Если в объекте содержались данные, то они пропадут.
function ToString:string;   Возвращает строку, содержащую текстовое представление JSON объекта.
procedure SaveToFile(const AFileName: string); Сохраняет текстовое представление JSON объекта в файл.
procedure SaveToStream(AStream: TStream Сохраняет текстовое представление JSON объекта в поток.
function IsNameExists(const AName:string); Возвращает True, если в объекте имеется свойство с указанным именем.
function IsNameValueExists(const Name, Value: string): boolean; Возвращает True, если в объекте имеется свойство с указанным именем и его значение совпадает с заданным.
function Count:Integer; Возвращает общее количество свойств JSON объекта.
property Names[AIndex:Integer]:string; Возвращает имя свойства с указанным индексом.
Property ValueType[IndexOrName:Variant]:TfrJSONType; Возвращает тип свойства с указанным индексом или именем. Тип может быть одним из следующих значений:

– jsUnknown – тип свойства неизвестен (произошла ошибка парсинга или свойство с таким индексом или именем отсутствует).
– jsNumber – свойство имеет числовой тип.
– jsString – свойство имеет строковый тип.
– jsBoolean – свойство имеет булевский тип (true/false).
– jsNull –тип свойства это null (значение не задано).
– jsList – тип свойства это массив JSON.
– jsObject – тип свойства это вложенный объект JSON.


Свойства ниже позволяют получить в том или ином виде значения объекта JSON. Во всех этих свойствах индексирование осуществляется либо по номеру свойства в списке свойств, либо по его имени. Отметим, что AsString[‘1’] <> AsString[1]. Также, если значение свойства попробовать получить неподходящим методом, то могут возникать ошибки и возвращаться неверные значения свойств. Например, если свойство имеет тип «Объект» или «Массив», то при попытке получить его значение при помощи свойства AsString - вам вернется пустая строка. В остальных случаях AsString вернет строковое представление значения свойства.

Свойства и методы класса Описание
property AsObject[IndexOrName: Variant]: TfrxJSON; Позволяет получить вложенный объект JSON.
property AsArray[IndexOrName: Variant]: TfrJSONArray; Позволяет получить вложенный массив JSON.
Property AsString[IndexOrName: Variant]: string; Позволяет получить значение свойства в виде строки. Этот метод получения можно применять к свойствам других типов, кроме объектов и массивов. Для логических значений будет возвращена строка, содержащая значение «True» или «False». Для чисел с дробной частью в качестве разделителя дробной части всегда, независимо от региональных настроек, будет использована точка.
property AsNumber[IndexOrName: Variant]: Extended; Позволяет получить значение свойства в виде числа. Если свойство имеет нечисловой тип, то вернется 0.
property AsBoolean[IndexOrName: Variant]: Boolean; Позволяет получить значение свойства в виде логического значения. Если свойство имеет другой тип, то вернется False. 


Чтобы вернуть поле типа null вам придётся немного постараться. Вообще такого метода нет. Однако можно воспользоваться свойством ValueType, которое для поля типа null вернет jsNull, либо воспользоваться свойством AsString, которое вернет строку ‘null’.

Рассмотрим подробнее методы для манипуляций со свойствами объекта.

Методы класса Описание
Procedure Delete(AName: String);   Удаление свойство объекта с указанным именем.
function AddObject(const AName: string): Integer Добавляет в объект дочерний пустой объект с указанным именем.
function AddArray(const AName: string): Integer; Добавляет в объект пустой массив с указанным именем.
function AddString(const AName, AValue: string): Integer; Добавляет в объект строку с указанным именем.
function AddBool(const AName: string; AValue: boolean): Integer; Добавляет в объект логическое значение с указанным именем.
function AddNumber(const AName: string; AValue:Extended): Integer; Добавляет в объект числовое вещественное значение с указанным именем.
function AddInteger(const AName: string; AValue:Integer): Integer; Добавляет в объект числовое целое значение с указанным именем.
function AddNull(const AName: string): Integer; Добавляет в объект значение null с указанным именем.


Методы возвращают целое число, которое является индексом добавленного элемента в списке всех полей.

Все методы, добавляющие поля в объект, не контролируют наличие существующих полей с такими же именами. Данная процедура возложена на программиста, использующего данный класс. Такой контроль необходим, чтобы в выходном JSON не появлялись несколько полей с одинаковыми именами. Поведение системы в таком случае не определено – это зависит от парсера, который далее будет разбирать полученный JSON. Это значит, что в работе может оказаться любое из значений с одинаковыми именами! 

Класс TJSONArray инкапсулирует в себе методы и свойства для работы с массивами JSON. В принципе, основные свойства те же, однако обращаться к ним можно только лишь по индексу. Поддерживаются только операции доступа к любому элементу, получение его значения, удаление любого элемента. Перемещать элементы по массиву, изменить их тип нельзя. Рассмотрим свойства и методы для получения значений элементов в этом классе.

Свойства и методы класса Описание
function Count:Integer; Возвращает количество элементов массива.
property ValueType[AIndex: Integer]: TfrJSONType; Возвращает тип элемента массива с заданным индексом.
property AsObject[AIndex: Integer]: TfrJSONObject; Возвращает объект, дающий доступ к элементу массива как к объекту JSON.
Property AsArray[AIndex: Integer]: TfrJSONArray Возвращает объект, дающий доступ к элементу массива как к массиву JSON.
property AsString[AIndex: Integer]: string; Возвращает значение элемента массива как строку.
property AsNumber[AIndex: Integer]: Extended; Возвращает значение элемента массива как число.
property AsBoolean[AIndex: Integer]: Boolean; Возвращает значение элемента массива как логическое значение.


Как и в объекте надо внимательно подходить к извлечению значений элементов массива. В зависимости от типа значения следует использовать соответствующий метод для его получения.

Ниже описаны методы для манипуляции элементами JSON массива.

Свойства и методы класса Описание
procedure Delete(AIndex:Integer); Удаление элемента массива c указанным индексом.
function AddObject: Integer; Добавляет вложенный JSON объект в конец массива.
function AddArray: Integer; Добавляет вложенный JSON массив в конец массива.
function AddString(const AValue: string): Integer; Добавляет строку в конец массива.
function AddBool(AValue: boolean): Integer; Добавляет логическое значение в конец массива.
function AddNumber(AValue:Extended): Integer; Добавляет вещественное число в конец массива.
function AddInteger(AValue: Integer): Integer; Добавляет целое число в конец массива.
function AddNull: Integer; Добавляет null в конец массива.


Все эти методы возвращают позицию элемента, добавленного в массив. В середину массива вставить элемент нельзя – это ограничение базовых классов в System.JSON.

 


 

Особенности использования классов для доступа JSON

При использовании объектов следует учесть, что после использования объектов-оберток все их необходимо удалять из кода. 

Обратите внимание, что если вы хотите получить какой-то дочерний объект или массив, а там вместо него находится скалярное значение или вообще значения нет, то свойство вернет nil, и при дальнейшем обращении к такому свойству возникнет исключение. Не стоит использовать методы, которые не предназначены для работы со значениями определённых типов, хранящимися в элементе, чтобы получить эти значения. 

Также следует учесть, что имена свойств объектов чувствительны к регистру, т.е. «Item1» и «item1» — это разные имена!

Можно задавать значения элементам объектов, только если тип этих значений соответствует типу, который задан у элемента. Если вам надо переопределить тип элемента, то удалите его и добавьте новый с таким же именем и нужным вам типом. Ввод новых значений у элементов массивов невозможно. Необходимо удалить массив JSON и потом создать его заново с нужными вам значениями.

Давайте воспользуемся классами из описания выше, и напишем скрипт, который извлекает данные (без информации о типах) из XML файла и помещает их в объект JSON.

procedure XMLToJSON;
var XML: TfrXMLDocument;
 xi, xi1: TfrXMLItem;
 I, J: Integer;
 JO: TfrJSONObject;
 JA, JA1: TfrJSONArray;
 SL: TStringList;
begin
 XML:=TfrXMLDocument.Create;
 XML.LoadFromFile('.\..\..\data\country.xml'); 
 JO:=TfrJSONObject.Create('{}');
 JO.AddArray('data');
 JA:=JO.AsArray['data'];
 xi:=X.Root[1];
 xi1:=xi.Items[0];
 SL:=TStringList.Create;
 xi1.GetParamNames(SL);
 for I:=0 to xi.Count - 1 do begin
 xi1:=xi.Items[I];
 JA1:=JA.AsArray[JA.AddArray];
 for J:=0 to SL.Count-1 do 
 JA1.AddString(xi1.Prop[SL[J]]);
 JA1.Free;
 end;
 JA.Free;
 JO.SaveToFile('.\..\..\data\country.json');
 JO.Free;
 SL.Free; 
end; 


И попробуем на основании полученных данных построить отчет (предварительно положите на страницу отчета соответствующие компоненты MasterData и Memo):

var J:TfrJSONObject;
 A:TfrJSONArray;
 curr:Integer;
 
procedure MasterData1OnBeforePrint(Sender: TfrxComponent);
var A1:TfrJSONArray;
begin
 A1:=A.AsArray(curr);
 Memo1.Memo.Text:=A1.AsString[0];
 Memo2.Memo.Text:=A1.AsString[1];
 Memo3.Memo.Text:=A1.AsString[2];
 Memo4.Memo.Text:=A1.AsString[3];
 Memo5.Memo.Text:=A1.AsString[4];
 Memo6.Memo.Text:=A1.AsString[5];
 A1.Free;
 curr := curr + 1;
end;
 
begin
 J:=TfrJSONObject.Create('');
 J.LoadFromFile('.\..\..\data\country.json');
 A:=J.AsArray[0];
 MasterData1.RowCount:=A.Count;
 curr:=0;
end.

 

Отчет успешно построен


Вы также можете использовать эти объекты и в коде Delphi – достаточно подключить модули frXML и frJSON в операторе uses. 

 


 

Заключение

В данной статье мы изучили применение классов для работы с JSON и XML в FastReport. Если вы занимаетесь разработкой, и не только отчетов, и в Delphi, и в Lazarus, то применение этих классов позволит уменьшить проблемы при переходе от одной среды к другой – в обоих IDE поведение будет одно и тоже. 

Для вашего удобства мы приложили к статье два примера, иллюстрирующие построение отчетов на основе XML и JSON. Их можно открыть запускать сразу в FastReportDemo, входящем в состав поставки FastReport. Возможно, перед запуском отчеты придется настроить – указать правильный путь к файлу данных «country.xml» - он находится как в составе DemoCenter, так и в папке с примерами. 

VCL FastScript JSON Script Delphi Отчет
24 апреля 2025

Как открыть и конвертировать файл FP3 с помощью МоиОтчеты Конвертер

Рассказываем о формате FP3, который используется для готовых отчётов в бизнес-приложениях, и о возможности конвертировать такие файлы в различные форматы с помощью МоиОтчеты Конвертер.
21 апреля 2025

Работа с компонентом TfrShellTreeView в FastReport VCL

В данной статье мы рассмотрим компонент TfrShellTreeView. Он предназначен для отображения элементов файловой системы и частично является аналогом компонентов TDirectoryListBox, TDirectoryOutline и TShellTreeView.
21 апреля 2025

Как работают RFID-метки в FastReport VCL

В этой статье мы рассмотрим принцип работы RFID-меток с новым объектом TfrxDeviceCommand в FastReport VCL с релизом 2025.2.