Почему веб-сервис 1С не возвращает таблицу значений и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке интеграционных решений на платформе 1С программисты часто сталкиваются с необходимостью передать табличные данные через SOAP-веб-сервисы. Попытка вернуть стандартный объект типа ТаблицаЗначений напрямую из метода веб-сервиса гарантированно приводит к ошибкам вроде "Неверный формат" или "Ошибка инициализации библиотеки модулей". Давайте подробно разберем, почему возникают эти сложности, проанализируем ошибки из практики разработчиков и рассмотрим три надежных способа решения этой задачи.

Выясняем причины: почему веб-сервис выдает ошибку?

Для начала проанализируем ситуацию, с которой часто сталкиваются разработчики при первой настройке веб-сервисов. Платформа 1С строго контролирует типы данных, передаваемые через SOAP-протокол. Основные причины сбоев заключаются в следующем:

  1. Несоответствие типов данных: Если в конфигураторе для операции веб-сервиса указан тип возвращаемого значения ValueStorage (соответствует ХранилищеЗначения), то платформа ожидает на выходе именно этот объект. Попытка вернуть строку XML, JSON или сырой XDTO-объект вызовет конфликт типов на уровне сериализации SOAP-конверта.
  2. Прямая передача ссылочных типов: В запросах 1С мы часто выбираем ссылки на элементы справочников или документов (например, Субконто1 или Договор). Ссылки 1С являются внутренними типами платформы. Внешний клиент (будь то SoapUI, PHP, Java или C#) ничего не знает о структуре ссылок 1С. Любые ссылки перед отправкой необходимо преобразовывать в простые типы: строки, числа или уникальные идентификаторы (UUID).
  3. Ограничения автоматической сериализации: Платформа 1С не умеет «на лету» конвертировать типы ТаблицаЗначений, Массив или Структура в стандартный XML-вид SOAP-сообщения, если структура этих данных заранее не описана в метаданных (XDTO-пакете) веб-сервиса.
  4. Кэширование схемы WSDL клиентом тестирования: Если вы изменили тип возвращаемого значения в конфигураторе (например, с ValueStorage на string), SoapUI или другой клиент может продолжать слать запросы по старой схеме из своего кэша. Это приводит к ложным сообщениям об ошибке "Неверный формат", даже если код написан абсолютно верно. Всегда выполняйте принудительное обновление схемы (например, Update Definition в SoapUI) после изменений в 1С.

Давайте подробно разберем три основных и правильных способа передачи табличных данных из веб-сервиса 1С.

Способ 1. Использование пакетов XDTO (Классический и строгий SOAP-путь)

Использование фабрики XDTO — это наиболее правильный и документированный стандарт разработки веб-сервисов. Этот способ делает структуру возвращаемой таблицы прозрачной для внешнего клиента, так как все типы данных будут описаны в WSDL-схеме.

Рассмотрим по шагам процесс реализации этого метода:

  1. Создадим в конфигураторе новый Пакет XDTO (например, с пространством имен http://www.MyCompany.ru/Exchange).
  2. Внутри пакета определим два типа объектов:
    • СтрокаТаблицы (тип объекта XDTO). Добавим свойства: ИИН (тип string), Наименование (тип string), Договор (тип string), Сумма (тип decimal).
    • СписокСтрок (тип объекта XDTO). Добавим свойство Строка, укажем для него тип СтрокаТаблицы (из нашего пакета) и установим свойство Максимальное количество в значение -1. Это указывает на то, что свойство является массивом.
  3. В свойствах операции нашего веб-сервиса укажем возвращаемое значение: выберем тип СписокСтрок из созданного нами пакета XDTO.

Теперь напишем код в модуле веб-сервиса, который будет преобразовывать выборку из запроса в структуру XDTO:


Функция ПолучитьДанныеКонтрагентов(КомментарийИд) Экспорт

    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Контрагенты.ИдентификационныйКодЛичности КАК ИИН,
        |   ТиповойОстатки.Субконто1 КАК Контрагент,
        |   ТиповойОстатки.Субконто3 КАК Договор,
        |   ТиповойОстатки.СуммаОстатокКт КАК СуммаОстаток
        |ИЗ
        |   РегистрБухгалтерии.Типовой.Остатки(&НаДату, Счет В ИЕРАРХИИ (&Счет), , ) КАК ТиповойОстатки
        |   ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
        |   ПО ТиповойОстатки.Субконто1 = Контрагенты.Ссылка
        |ГДЕ
        |   Контрагенты.Комментарий ПОДОБНО &Комментарий";
        
    Запрос.УстановитьПараметр("НаДату", ТекущаяДата());
    
    СчетаСписком = Новый СписокЗначений;
    СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3511"));
    СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3517"));
    Запрос.УстановитьПараметр("Счет", СчетаСписком);
    Запрос.УстановитьПараметр("Комментарий", КомментарийИд);
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    // Получаем типы объектов из фабрики XDTO по определенному нами пространству имен
    ТипСписка = ФабрикаXDTO.Тип("http://www.MyCompany.ru/Exchange", "СписокСтрок");
    ТипСтроки = ФабрикаXDTO.Тип("http://www.MyCompany.ru/Exchange", "СтрокаТаблицы");
    
    // Создаем корневой объект списка
    ВозвращаемыйОбъект = ФабрикаXDTO.Создать(ТипСписка);
    
    Пока Выборка.Следующий() Цикл
        // Создаем отдельный XDTO-объект для строки таблицы
        НоваяСтрока = ФабрикаXDTO.Создать(ТипСтроки);
        
        НоваяСтрока.ИИН = Выборка.ИИН;
        // Важно: преобразуем ссылки 1С в строковое представление!
        НоваяСтрока.Наименование = Строка(Выборка.Контрагент);
        НоваяСтрока.Договор = Строка(Выборка.Договор);
        НоваяСтрока.Сумма = Выборка.СуммаОстаток;
        
        // Добавляем заполненный объект строки в наш результирующий список
        ВозвращаемыйОбъект.Строка.Добавить(НоваяСтрока);
    КонецЦикла;
    
    Возврат ВозвращаемыйОбъект;

КонецФункции

Способ 2. Передача данных в формате JSON-строки (Универсальный и легкий путь)

Если вам не хочется тратить время на проектирование сложных схем XDTO-пакетов или если на принимающей стороне работает веб-приложение, которому проще обрабатывать текстовый формат, идеальным решением будет передача JSON-строки. JSON поддерживается абсолютно всеми языками программирования.

Для реализации этого метода выполним следующие шаги:

  1. В свойствах операции веб-сервиса установим тип возвращаемого значения в значение string (из стандартного пространства имен XML-схемы http://www.w3.org/2001/XMLSchema).
  2. В коде преобразуем выборку данных в массив простых структур 1С, так как напрямую ТаблицаЗначений не может быть сериализована в JSON стандартной процедурой ЗаписатьJSON без потери типов и структуры.

Посмотрим на пример реализации данного алгоритма:


Функция ПолучитьДанныеВФорматеJSON(КомментарийИд) Экспорт

    // Выполняем аналогичный запрос к базе данных
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Контрагенты.ИдентификационныйКодЛичности КАК ИИН,
        |   ТиповойОстатки.Субконто1 КАК Контрагент,
        |   ТиповойОстатки.Субконто3 КАК Договор,
        |   ТиповойОстатки.СуммаОстатокКт КАК СуммаОстаток
        |ИЗ
        |   РегистрБухгалтерии.Типовой.Остатки(&НаДату, Счет В ИЕРАРХИИ (&Счет), , ) КАК ТиповойОстатки
        |   ВНУТРЕННЕЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты
        |   ПО ТиповойОстатки.Субконто1 = Контрагенты.Ссылка
        |ГДЕ
        |   Контрагенты.Комментарий ПОДОБНО &Комментарий";
        
    Запрос.УстановитьПараметр("НаДату", ТекущаяДата());
    
    СчетаСписком = Новый СписокЗначений;
    СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3511"));
    СчетаСписком.Добавить(ПланыСчетов.Типовой.НайтиПоКоду("3517"));
    Запрос.УстановитьПараметр("Счет", СчетаСписком);
    Запрос.УстановитьПараметр("Комментарий", КомментарийИд);
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    // Создаем массив структур, который легко сериализуется в JSON
    МассивРезультатов = Новый Массив;
    
    Пока Выборка.Следующий() Цикл
        СтруктураСтроки = Новый Структура;
        СтруктураСтроки.Вставить("ИИН", Выборка.ИИН);
        // Не забываем преобразовать ссылки в строку
        СтруктураСтроки.Вставить("Наименование", Строка(Выборка.Контрагент));
        СтруктураСтроки.Вставить("Договор", Строка(Выборка.Договор));
        СтруктураСтроки.Вставить("Сумма", Выборка.СуммаОстаток);
        
        МассивРезультатов.Добавить(СтруктураСтроки);
    КонецЦикла;
    
    // Выполняем запись массива структур в JSON-строку
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, МассивРезультатов);
    СтрокаРезультат = ЗаписьJSON.Закрыть();
    
    Возврат СтрокаРезультат;

КонецФункции

Важное примечание: После изменения типа возвращаемого значения операции на string обязательно обновите описание WSDL-интерфейса в SoapUI с помощью команды Update Definition, иначе SoapUI продолжит ожидать старый формат и выдавать ошибку разбора XML.

Способ 3. Использование Хранилища Значения (Для интеграции типа «1С — 1С»)

Если вызывающей стороной (клиентом вашего веб-сервиса) является другая база данных 1С, то вы можете использовать системный тип ХранилищеЗначения. Внутри него можно передать полноценный бинарный объект 1С (включая готовую таблицу значений с типизированными колонками).

При таком подходе не нужно заботиться о ручном преобразовании ссылок и сериализации — платформа 1С на принимающей стороне сама восстановит исходную таблицу значений из бинарных данных.

Рассмотрим, как настроить и закодировать этот процесс:

  1. Установим в свойствах возвращаемого значения операции веб-сервиса тип ValueStorage (из пространства имен http://v8.1c.ru/8.1/data/core).
  2. Напишем код подготовки данных на стороне сервера-источника:

Функция ПолучитьТаблицуЧерезХранилище(КомментарийИд) Экспорт

    // Создаем и описываем колонки Таблицы Значений
    ТЗ = Новый ТаблицаЗначений;
    ТЗ.Колонки.Добавить("ИИН", Новый ОписаниеТипов("Строка"));
    ТЗ.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
    ТЗ.Колонки.Добавить("Договор", Новый ОписаниеТипов("Строка"));
    ТЗ.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
    
    // Запрос к данным
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Контрагенты.ИдентификационныйКодЛичности КАК ИИН,
        |   ТиповойОстатки.Субконто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С. Подведем итоги совместной работы и выберем оптимальный вариант для вашей задачи:

← На главную