Прикреплен файл: WizardSimple.zip
В предыдущей статье рассмотрели использование JS и JSON при работе с сервисами из прикладной разработки. В этой статье остановлюсь подробнее на использовании формата JSON для хранения данных на примере имитации заполнения табличной части в мастерах действий.
На своих проектах часто разрабатываем адаптеры к видам сведений СМЭВ. Адаптеры реализуются с помощью мастеров действий, где запрашиваются все необходимые данные для отправки в СМЭВ. Результатом работы мастера является xml запрос. Часто в запросах встречаются повторение каких-либо элементов, например, указание в одном запросе нескольких персон, объектов недвижимости, вложение документов, и т.п. Поэтому возникла потребность имитации заполнения табличных частей в мастерах действий.
Для этого на этап выносим повторяющиеся данные, например, фамилия, имя, отчество гражданина, и добавляем параметр логического типа: «Добавить еще данные». Этот этап зацикливаем путем переопределения следующего шага текущим на кнопке Далее. В события мастера действий Начало, Next и Previous добавляется логика для прохождения всех итераций вперед и назад, которая работает с индексом.
При этом способе информация с зацикленного этапа хранится в отдельном параметре в виде текста с разделителями. Для работы со значениями параметров используется IStringList. Значения с одного этапа сохраняются с одним типом разделителя, а между итерациями используется другой тип разделителя. Их может быть несколько, для реализации сложных вложенных циклов.
Ниже приведен пример данных, полученных из цикла, состоящего из нескольких этапов, один из которых так же зациклен.
Д000003*;==;*Д000006*;==;*Д000008#$!!$#3#$!!$#1#$!!$#Д000008*;==;*Д000013*;==;*
Д000015#$!!$#6#$!!$#1#$!!$#Д000001*;==;*Д000004#$!!$#2#$!!$#1#$!!$#Нежилое#$!!$#
#$!!$##$!!$#Здание?;==;?#$!!$##$!!$##$!!$##$!!$##$!!$##$!!$#Д000003*;==;*Д000004
#$!!$#2#$!!$#1#$!!$#Квартира#$!!$##$!!$##$!!$#Здание?;==;?Д000007*;==;*Д000005
*;==;*Д000008#$!!$#3#$!!$#1#$!!$#Д000017*;==;*Д000019*;==;*Д000015#$!!$#3#$!!$#1
#$!!$##$!!$##$!!$##$!!$##$!!$##$!!$##$!!$#Земельный участок
На простых адаптерах такой вариант реализации хорошо отрабатывал. Но для более сложных структур, например, когда требовалось сделать цикл из нескольких этапов с вложенными внутренними циклами, были сложности с разработкой, а потом и с отладкой. В виде списка данные с таких этапов выглядели непонятно и громоздко, тяжело было отследить значения параметров.
В результате, решили воспользоваться форматом, изначально предназначенном для хранения структурированной информации в текстовом виде - JSON.
Формат JSON – более удобен, наглядно видны все полученные данные с этапов, в т. ч. массивы с множественных этапов. Так же существуют всевозможные редакторы для работы с ним, что очень удобно при отладке (универсальные функции для работы с форматом JSON можно посмотреть во вложении к первой части статьи).
В итоге с зацикленных этапов получаем массив объектов, представляющих данные, введенные на шаге, при этом каждое поле имеет явное наименование.
Ниже приведен пример данных, полученных из идентичного цикла этапов, указанного в примере с первой реализации.
Пример (стало):
{
"Data": [
{
"extractSubject_RealtyType": "Здание",
"_DeleteExtractSubject": false,
"extractSubject_ParcelCategory": null,
"extractSubject_ParcelUsageType": null,
"_extractSubject_AddRealtyType": true,
"extractSubject_BuildingPurpose": [
" Д000001",
" Д000003"
],
"extractSubject_RoomChoice": null,
"extractSubject_OtherObjectType": null,
"extractSubject_OtherNotes": null
},
{
"extractSubject_RealtyType": "Земельный участок",
"_DeleteExtractSubject": false,
"extractSubject_ParcelCategory": [
" Д000003",
" Д000004",
" Д000006"
],
"extractSubject_ParcelUsageType": [
" Д000013",
" Д000007",
" Д000016"
],
"_extractSubject_AddRealtyType": true,
"extractSubject_BuildingPurpose": null,
"extractSubject_RoomChoice": null,
"extractSubject_OtherObjectType": null,
"extractSubject_OtherNotes": null
}
],
"Index": 2
}
Функции, которые использовались в мастерах действий, основаны на преобразовании списка параметров мастера действий IWizardParam в объект JS и обратно. Ниже приведен упрощенный пример данных функций, которые могут работать c IList. Пример мастера действия с одним зацикленным этапом и функции, которые использовались, будут во вложении к статье.
Преобразование списка IList в объект и обратно в мастере действий:
// Пример части кода на событии Next МД
Params = Wizard.Params
Index = Params.ValueByName('Index').Value
if not Assigned(Params.ValueByName('Person_JSON').Value)
// Создать объекты для работы с js
PersonArray = CreateJavascriptArray()
else
PersonArray = JSONToObject(Params.ValueByName('Person_JSON').Value)
endif
ResultObject = CreateJavascriptObject()
// Создать списки с данными
PersonList = CreateList()
PersonList.Add('FirstName'; Params.ValueByName('FirstName').Value)
PersonList.Add('FamilyName'; Params.ValueByName('FamilyName').Value)
PersonList.Add('Patronymic'; Params.ValueByName('Patronymic').Value)
PersonList.Add('AddPerson'; Params.ValueByName('AddPerson').Value)
LocationList = CreateList()
LocationList.Add('Country'; Params.ValueByName('Country').Value)
LocationList.Add('City'; Params.ValueByName('City').Value)
LocationList.Add('Street'; Params.ValueByName('Street').Value)
// Сформировать JS объект из IList
PersonObject = CreateJSObjectFromList(PersonList)
LocationObject = CreateJSObjectFromList(LocationList)
// Установить значения
SetJSObjectProperty(ResultObject; "PersonList"; PersonObject)
SetJSObjectProperty(PersonObject; "Location"; LocationObject)
SetJSValueInArray(PersonArray; Index; PersonObject)
Params.ValueByName('Person_JSON').Value = JSObjectToJSON(PersonArray)
// Пример кода на событии Previous
FullObject = JSONToObject(Params.ValueByName('Person_JSON').Value)
PersonData = GetJSValueFromArray(FullObject; Params.ValueByName('Index').Value)
Params.ValueByName('FirstName').Value = GetJSObjectProperty(PersonData; 'FirstName')
Params.ValueByName('FamilyName').Value = GetJSObjectProperty(PersonData; 'FamilyName')
Params.ValueByName('Patronymic').Value = GetJSObjectProperty(PersonData; 'Patronymic')
LocationData = GetJSObjectProperty(PersonData; 'Location')
Params.ValueByName('Country').Value = LocationData.Country
Params.ValueByName('City').Value = LocationData.City
Params.ValueByName('Street').Value = LocationData.Street
Пример преобразования формата JSON в IList:
JSON = '{"PersonList":{"Name":"Петров","Surname":"Александр","Patronymic":"Константинович","Location":{"Country":"Россия","City":"Москва","Street":"Достоевского"}}}'
PersonList = CreateList()
PersonList.Add('Name'; '')
PersonList.Add('Surname'; '')
PersonList.Add('Patronymic'; '')
FullObject = JSONToObject(JSON)
Person = GetJSObjectProperty(FullObject; "PersonList")
PersonList = FillListFromObject(Person; PersonList)
ShowMessage(PersonList.Values(1))
С помощью формата JSON можно решать и другие задачи, где может понадобиться:
Таким образом JS можно расширить возможности прикладной. Надеюсь наработки будут полезны в вашей работе.
В рекомендациях по разработке мастеров действий вендор не рекомендует заполнять табличные части путем зацикливания этапа, т.к. при необходимости изменить уже введенные данные, придется долго возвращаться назад. Вместо этого он рекомендует:
К этому списку можно добавить еще третий вариант, заполнение таблиц с помощью диалога с табличной частью, но тут потребуются доработки в веб-модуле, чтобы этот вариант заработал в веб-доступе. Причем в новых версиях веб-доступа возможно доработки уже и не потребуются.
Можете пояснить, почему все же остановились на варианте с зацикливанием этапа и как решаете проблему с необходимостью возврата назад?
Поясню зачем остановились на варианте с зацикливанием этапа:
По проблеме с возвратом назад - имеете ввиду, что если, например, этап зациклен 10 раз и надо изменить данные в первой итерации - то придется девять раз нажимать назад? Да, сейчас так и будет работать.
Вложенные массивов в объект JSON не предусмотрен?
Рустам, Сам по себе JSON может хранить деревья из массивов и объектов.
А вот как вы будете эти деревья строить и работать с ними, зависит от вашей реализации.
Михаил, я про готовую функцию "SetJSObjectProperty", которую предоставил автор. На примерах, он создает объект, параметры которого являются вложенные объекты или строки. А как обстоят дела, например, с добавлением в параметр массива из объектов?
Разобрался, создать массив можно:
JavascriptProcessor = GetJavascriptProcessor() jsArray= JavascriptProcessor.Eval("([])")
Выполняя запрос, в функции SendHTTPRequestJSON возникает ошибка "Сбой скачивания указанного ресурса." в 19 строке. Я подозреваю что в одном из параметров, большой объем информации. При уменьшении текста, все срабатывает, но проблема в том что нужно передать весь текст. Кто сталкивался с таким? Как решали проблему?
Авторизуйтесь, чтобы написать комментарий