При разработке интеграционных решений на платформе 1С программисты часто сталкиваются с необходимостью передать табличные данные через SOAP-веб-сервисы. Попытка вернуть стандартный объект типа ТаблицаЗначений напрямую из метода веб-сервиса гарантированно приводит к ошибкам вроде "Неверный формат" или "Ошибка инициализации библиотеки модулей". Давайте подробно разберем, почему возникают эти сложности, проанализируем ошибки из практики разработчиков и рассмотрим три надежных способа решения этой задачи.
Для начала проанализируем ситуацию, с которой часто сталкиваются разработчики при первой настройке веб-сервисов. Платформа 1С строго контролирует типы данных, передаваемые через SOAP-протокол. Основные причины сбоев заключаются в следующем:
ValueStorage (соответствует ХранилищеЗначения), то платформа ожидает на выходе именно этот объект. Попытка вернуть строку XML, JSON или сырой XDTO-объект вызовет конфликт типов на уровне сериализации SOAP-конверта.Субконто1 или Договор). Ссылки 1С являются внутренними типами платформы. Внешний клиент (будь то SoapUI, PHP, Java или C#) ничего не знает о структуре ссылок 1С. Любые ссылки перед отправкой необходимо преобразовывать в простые типы: строки, числа или уникальные идентификаторы (UUID).ТаблицаЗначений, Массив или Структура в стандартный XML-вид SOAP-сообщения, если структура этих данных заранее не описана в метаданных (XDTO-пакете) веб-сервиса.ValueStorage на string), SoapUI или другой клиент может продолжать слать запросы по старой схеме из своего кэша. Это приводит к ложным сообщениям об ошибке "Неверный формат", даже если код написан абсолютно верно. Всегда выполняйте принудительное обновление схемы (например, Update Definition в SoapUI) после изменений в 1С.Давайте подробно разберем три основных и правильных способа передачи табличных данных из веб-сервиса 1С.
Использование фабрики XDTO — это наиболее правильный и документированный стандарт разработки веб-сервисов. Этот способ делает структуру возвращаемой таблицы прозрачной для внешнего клиента, так как все типы данных будут описаны в WSDL-схеме.
Рассмотрим по шагам процесс реализации этого метода:
http://www.MyCompany.ru/Exchange).ИИН (тип string), Наименование (тип string), Договор (тип string), Сумма (тип decimal).Строка, укажем для него тип СтрокаТаблицы (из нашего пакета) и установим свойство Максимальное количество в значение -1. Это указывает на то, что свойство является массивом.СписокСтрок из созданного нами пакета XDTO.Теперь напишем код в модуле веб-сервиса, который будет преобразовывать выборку из запроса в структуру XDTO:
Функция ПолучитьДанныеКонтрагентов(КомментарийИд) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.ИдентификационныйКодЛичности КАК ИИН,
| ТиповойОстатки.Субконто1 КАК Контрагент,
| ТиповойОстатки.Субконто3 КАК Договор,
| ТиповойОстатки.СуммаОстатокКт КАК СуммаОстаток
|ИЗ
| РегистрБухгалтерии.Типовой.Остатки(&НаДату, Счет В ИЕРАРХИИ (&Счет), , ) КАК ТиповойОстатки
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО ТиповойОстатки.Субконто1 = Контрагенты.Ссылка
|ГДЕ
| Контрагенты.Комментарий ПОДОБНО &Комментарий";
Запрос.УстановитьПараметр("НаДату", ТекущаяДата());
СчетаСписком = Новый СписокЗначений;
СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3511"));
СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3517"));
Запрос.УстановитьПараметр("Счет", СчетаСписком);
Запрос.УстановитьПараметр("Комментарий", КомментарийИд);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// Получаем типы объектов из фабрики XDTO по определенному нами пространству имен
ТипСписка = ФабрикаXDTO.Тип("http://www.MyCompany.ru/Exchange", "СписокСтрок");
ТипСтроки = ФабрикаXDTO.Тип("http://www.MyCompany.ru/Exchange", "СтрокаТаблицы");
// Создаем корневой объект списка
ВозвращаемыйОбъект = ФабрикаXDTO.Создать(ТипСписка);
Пока Выборка.Следующий() Цикл
// Создаем отдельный XDTO-объект для строки таблицы
НоваяСтрока = ФабрикаXDTO.Создать(ТипСтроки);
НоваяСтрока.ИИН = Выборка.ИИН;
// Важно: преобразуем ссылки 1С в строковое представление!
НоваяСтрока.Наименование = Строка(Выборка.Контрагент);
НоваяСтрока.Договор = Строка(Выборка.Договор);
НоваяСтрока.Сумма = Выборка.СуммаОстаток;
// Добавляем заполненный объект строки в наш результирующий список
ВозвращаемыйОбъект.Строка.Добавить(НоваяСтрока);
КонецЦикла;
Возврат ВозвращаемыйОбъект;
КонецФункции
Если вам не хочется тратить время на проектирование сложных схем XDTO-пакетов или если на принимающей стороне работает веб-приложение, которому проще обрабатывать текстовый формат, идеальным решением будет передача JSON-строки. JSON поддерживается абсолютно всеми языками программирования.
Для реализации этого метода выполним следующие шаги:
http://www.w3.org/2001/XMLSchema).ТаблицаЗначений не может быть сериализована в JSON стандартной процедурой ЗаписатьJSON без потери типов и структуры.Посмотрим на пример реализации данного алгоритма:
Функция ПолучитьДанныеВФорматеJSON(КомментарийИд) Экспорт
// Выполняем аналогичный запрос к базе данных
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.ИдентификационныйКодЛичности КАК ИИН,
| ТиповойОстатки.Субконто1 КАК Контрагент,
| ТиповойОстатки.Субконто3 КАК Договор,
| ТиповойОстатки.СуммаОстатокКт КАК СуммаОстаток
|ИЗ
| РегистрБухгалтерии.Типовой.Остатки(&НаДату, Счет В ИЕРАРХИИ (&Счет), , ) КАК ТиповойОстатки
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО ТиповойОстатки.Субконто1 = Контрагенты.Ссылка
|ГДЕ
| Контрагенты.Комментарий ПОДОБНО &Комментарий";
Запрос.УстановитьПараметр("НаДату", ТекущаяДата());
СчетаСписком = Новый СписокЗначений;
СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3511"));
СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3517"));
Запрос.УстановитьПараметр("Счет", СчетаСписком);
Запрос.УстановитьПараметр("Комментарий", КомментарийИд);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
// Создаем массив структур, который легко сериализуется в JSON
МассивРезультатов = Новый Массив;
Пока Выборка.Следующий() Цикл
СтруктураСтроки = Новый Структура;
СтруктураСтроки.Вставить("ИИН", Выборка.ИИН);
// Не забываем преобразовать ссылки в строку
СтруктураСтроки.Вставить("Наименование", Строка(Выборка.Контрагент));
СтруктураСтроки.Вставить("Договор", Строка(Выборка.Договор));
СтруктураСтроки.Вставить("Сумма", Выборка.СуммаОстаток);
МассивРезультатов.Добавить(СтруктураСтроки);
КонецЦикла;
// Выполняем запись массива структур в JSON-строку
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, МассивРезультатов);
СтрокаРезультат = ЗаписьJSON.Закрыть();
Возврат СтрокаРезультат;
КонецФункции
Важное примечание: После изменения типа возвращаемого значения операции на string обязательно обновите описание WSDL-интерфейса в SoapUI с помощью команды Update Definition, иначе SoapUI продолжит ожидать старый формат и выдавать ошибку разбора XML.
Если вызывающей стороной (клиентом вашего веб-сервиса) является другая база данных 1С, то вы можете использовать системный тип ХранилищеЗначения. Внутри него можно передать полноценный бинарный объект 1С (включая готовую таблицу значений с типизированными колонками).
При таком подходе не нужно заботиться о ручном преобразовании ссылок и сериализации — платформа 1С на принимающей стороне сама восстановит исходную таблицу значений из бинарных данных.
Рассмотрим, как настроить и закодировать этот процесс:
ValueStorage (из пространства имен http://v8.1c.ru/8.1/data/core).
Функция ПолучитьТаблицуЧерезХранилище(КомментарийИд) Экспорт
// Создаем и описываем колонки Таблицы Значений
ТЗ = Новый ТаблицаЗначений;
ТЗ.Колонки.Добавить("ИИН", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Договор", Новый ОписаниеТипов("Строка"));
ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
// Запрос к данным
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Контрагенты.ИдентификационныйКодЛичности КАК ИИН,
| ТиповойОстатки.Субконто1 КАК Контрагент,
| ТиповойОстатки.Субконто3 КАК Договор,
| ТиповойОстатки.СуммаОстатокКт КАК СуммаОстаток
|ИЗ
| РегистрБухгалтерии.Типовой.Остатки(&НаДату, Счет В ИЕРАРХИИ (&Счет), , ) КАК ТиповойОстатки
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
| ПО ТиповойОстатки.Субконто1 = Контрагенты.Ссылка
|ГДЕ
| Контрагенты.Комментарий ПОДОБНО &Комментарий";
Запрос.УстановитьПараметр("НаДату", ТекущаяДата());
СчетаСписком = Новый СписокЗначений;
СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3511"));
СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3517"));
Запрос.УстановитьПараметр("Счет", СчетаСписком);
Запрос.УстановитьПараметр("Комментарий", КомментарийИд);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
НоваяСтрока = ТЗ.Добавить();
НоваяСтрока.ИИН = Выборка.ИИН;
НоваяСтрока.Наименование = Строка(Выборка.Контрагент);
НоваяСтрока.Договор = Строка(Выборка.Договор);
НоваяСтрока.Сумма = Выборка.СуммаОстаток;
КонецЦикла;
// Помещаем таблицу значений в ХранилищеЗначения
РезультатХранилище = Новый ХранилищеЗначения(ТЗ);
// Возвращаем именно ХранилищеЗначения, соответствующее типу ValueStorage
Возврат РезультатХранилище;
КонецФункции
Теперь проанализируем, как правильно получить и обработать эти данные на стороне базы-приемника (клиента):
Процедура ПолучитьДанныеИзВебСервисаИсточника()
// Подключаемся к веб-сервису через WSПрокси
Определение = Новый WSОпределения("http://172.16.103.89/Base/ws/StudentWS?wsdl", "Администратор", "Пароль");
Прокси = Новый WSПрокси(Определение, "http://www.MyCompany.ru/Exchange", "StudentWS", "StudentWSSoap");
// Вызываем метод веб-сервиса
РезультатХранилище = Прокси.ПолучитьТаблицуЧерезХранилище("Идентификатор");
// Проверяем тип пришедшего ответа
Если ТипЗнч(РезультатХранилище) = Тип("ХранилищеЗначения") Тогда
// Распаковываем исходную ТаблицуЗначений
ТаблицаЗначенийРезультат = РезультатХранилище.Получить();
Для Каждого СтрокаТЗ Из ТаблицаЗначенийРезультат Цикл
Сообщить("ИИН: " + СтрокаТЗ.ИИН + ", Контрагент: " + СтрокаТЗ.Наименование + ", Сумма: " + СтрокаТЗ.Сумма);
КонецЦикла;
Иначе
Сообщить("Пришли неожиданные данные!");
КонецЕсли;
КонецПроцедуры
Внимание: Не используйте способ с ValueStorage, если клиентское приложение написано на сторонних технологиях (PHP, C#, Python, Node.js). 1С сжимает данные внутри хранилища по своему собственному закрытому алгоритму, и внешние парсеры не смогут корректно распаковать этот бинарный буфер из SOAP-ответа.
Мы подробно разобрали основные способы передачи табличных данных из веб-сервисов 1С. Подведем итоги совместной работы и выберем оптимальный вариант для вашей задачи: