Работа с JSON на ISBL с помощью JavaScript. Часть 2

15 7

Прикреплен файл: 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 – более удобен, наглядно видны все полученные данные с этапов, в т. ч. массивы с множественных этапов. Так же существуют всевозможные редакторы для работы с ним, что очень удобно при отладке (универсальные функции для работы с форматом 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 можно расширить  возможности прикладной. Надеюсь наработки будут полезны в вашей работе.

Дмитрий Тарасов

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

  • Использовать параметр типа «Список записей справочника\объектов\электронных документов\пользователей».
  • Выбрать максимальное количество записей в табличной части и разместить соответствующее количество параметров на форме.

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

Можете пояснить, почему все же остановились на варианте с зацикливанием этапа и как решаете проблему с необходимостью возврата назад?

Татьяна Пушина

Поясню зачем остановились на варианте с зацикливанием этапа:

  • Согласна, что когда есть возможность, то надо использовать параметры типа список записей справочников, что мы и используем, и что было очевидно, поэтому этот вариант в статье не рассматривала.Если известно максимальное количество параметров и они адекватно влезают на форму - то же используем этот прием.
  • Мастера действий формируем согласно xsd схемам запросов, поэтому максимальное число вводимых повторений не известно (например, [0..*]), или бывает указано, но очень редко.
  • Часто бывает задача ввести данные граждан/организаций, например, ФИО, данные документа удостоверяющего личность, дату рождения, месторождения и еще другие данные в зависимости от вида сведений СМЭВ, таким образом на одном этапе по одному гражданину могут быть как строковые поля, так и дата, и записи справочника. При этом нет цели хранить эти данные в системе в справочном виде или получать их из системы. В справочниках хранится только общепринятые классификаторы, например, регионы, страны, документы удостоверяющие личность, и т.д.
  • Когда начинали разработку адаптеров для СМЭВ - делали еще для версии 4.9, тогда диалогов еще не было, и да, мастера удобны тем, что не надо дорабатывать специально для веб доступа.

По проблеме с возвратом назад - имеете ввиду, что если, например, этап зациклен 10 раз и надо изменить данные в первой итерации - то придется девять раз нажимать назад? Да, сейчас так и будет работать. 

Рустам Султанов

Вложенные массивов в объект JSON не предусмотрен?

Рустам Султанов: обновлено 15.11.2019 в 12:25
Рустам Султанов: обновлено 15.11.2019 в 12:26
Михаил Тарасов

Рустам,  Сам по себе JSON может хранить деревья из массивов и объектов.

А вот как вы будете эти деревья строить и работать с ними, зависит от вашей реализации.

Рустам Султанов

Михаил, я про готовую функцию "SetJSObjectProperty", которую предоставил автор. На примерах, он создает объект, параметры которого являются вложенные объекты или строки. А как обстоят дела, например, с добавлением в параметр массива из объектов?

Рустам Султанов: обновлено 19.11.2019 в 14:58
Рустам Султанов

Разобрался, создать массив можно:

JavascriptProcessor = GetJavascriptProcessor()

jsArray= JavascriptProcessor.Eval("([])")
Рустам Султанов: обновлено 27.11.2019 в 10:43
Дмитрий Бакаев

Выполняя запрос, в функции SendHTTPRequestJSON возникает ошибка "Сбой скачивания указанного ресурса." в 19 строке. Я подозреваю что в одном из параметров, большой объем информации. При уменьшении текста, все срабатывает, но проблема в том что нужно передать весь текст. Кто сталкивался с таким? Как решали проблему?

Авторизуйтесь, чтобы написать комментарий