Как передать сложные типы данных (Таблицу значений, Структуру) через Web-сервис в 1С:Предприятие?

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

При интеграции различных информационных баз на платформе 1С:Предприятие разработчики часто сталкиваются с задачей организации двустороннего обмена данными. Если передача простых типов (таких как строки, числа или булево) через Web-сервисы (SOAP) не вызывает затруднений, то при попытке отправить сложные структуры данных — например, ТаблицаЗначений или Структура, внутри которой находится еще одна таблица — система выдает ошибки несоответствия типов.

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

В чем заключается основная проблема передачи сложных типов?

Web-сервисы работают по строгому протоколу SOAP, где структура передаваемых данных описывается схемой XML/XDTO (файл WSDL). Тип данных ТаблицаЗначений является внутренним мутабельным типом платформы 1С. В глобальных стандартах XML нет прямого аналога для 1С-овской таблицы значений. Пытаясь передать объект ТаблицаЗначений напрямую в параметр метода Web-сервиса, мы неизбежно получим системную ошибку несоответствия типов.

Дополнительная сложность заключается в ссылочных типах данных. Если строки вашей таблицы содержат ссылки на элементы справочников или документов (например, СправочникСсылка.Регионы), то простая передача этих ссылок в другую базу бессмысленна. В базе-приемнике эти уникальные идентификаторы (GUID) либо будут отсутствовать (что приведет к появлению «битых» ссылок со значением «Объект не найден»), либо будут указывать на совершенно другие объекты, если конфигурации баз различаются.

Критика устаревших методов: ЗначениеВСтрокуВнутр() и XML-сериализация

Прежде чем перейти к рекомендуемому решению, разберем методы, которые часто советуют на форумах, и выясним, почему их использование в реальных проектах нежелательно:

  1. Функции ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр(): Этот способ преобразует объект 1С во внутренний системный формат строки. Он кажется простым, но имеет критический недостаток. Этот механизм работает исключительно в рамках абсолютно идентичных конфигураций и версий платформ. Если база-источник и база-приемник хоть немного отличаются по структуре метаданных, попытка десериализации гарантированно вызовет ошибку.
  2. Прямая XML-сериализация через СериализаторXDTO: Позволяет быстро получить XML-представление объекта. Однако, если конфигурации баз различаются (например, разные имена справочников или реквизитов), метод ПрочитатьXML() на стороне приемника завершится аварийно.

Лучшее современное решение: Сериализация в формат JSON

Использование формата JSON — это стандарт де-факто для обмена данными. JSON представляет собой легкий текстовый формат, не привязанный к конкретной конфигурации 1С. Он позволяет передавать структуры практически любой вложенности. Для автоматизации и быстрого построения такого обмена подойдет конструктор API для интеграции 1С в формате JSON.

Для успешной передачи таблицы значений через JSON нам необходимо выполнить три шага:

  1. Свернуть или обойти исходную таблицу значений, преобразовав её в Массив, состоящий из объектов типа Структура.
  2. В процессе обхода заменить все объектные ссылки (справочники, документы) на их текстовые представления (уникальные идентификаторы GUID или бизнес-ключи — код, артикул, ИНН).
  3. Сериализовать полученную структуру в строку JSON и передать ее через стандартный строковый параметр Web-сервиса.

Рассмотрим детально, как реализовать этот алгоритм на практике.

Реализация на стороне базы-источника (Отправитель)

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


&НаСервере
Функция ОтправитьДанныеЧерезВебСервис()
    
    // 1. Формируем исходную таблицу значений (например, запросом)
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Регионы.Ссылка КАК Ссылка,
        |   Регионы.Код КАК Код,
        |   Регионы.Наименование КАК Наименование,
        |   Регионы.КодРегиона КАК КодРегиона
        |ИЗ
        |   Справочник.Регионы КАК Регионы";
        
    ТаблицаРегионов = Запрос.Выполнить().Выгрузить();
    
    // 2. Преобразуем ТаблицуЗначений в Массив Структур с очисткой от ссылок
    МассивСтрок = Новый Массив;
    Для Каждого СтрокаТЗ Из ТаблицаРегионов Цикл
        СтрокаСтруктура = Новый Структура;
        СтрокаСтруктура.Вставить("Код", СтрокаТЗ.Код);
        СтрокаСтруктура.Вставить("Наименование", СтрокаТЗ.Наименование);
        СтрокаСтруктура.Вставить("КодРегиона", СтрокаТЗ.КодРегиона);
        
        // Вместо ссылки передаем ее строковый уникальный идентификатор (GUID)
        СтрокаСтруктура.Вставить("UID", Строка(СтрокаТЗ.Ссылка.УникальныйИдентификатор()));
        
        МассивСтрок.Добавить(СтрокаСтруктура);
    КонецЦикла;
    
    // 3. Формируем итоговую структуру для отправки
    СтруктураПакет = Новый Структура;
    СтруктураПакет.Vоставить("ИмяТаблицы", "Регионы");
    СтруктураПакет.Вставить("ДатаФормирования", ТекущаяДата());
    СтруктураПакет.Вставить("ДанныеТаблицы", МассивСтрок);
    
    // 4. Сериализуем структуру в JSON-строку
    ЗаписьJSON = Новый ЗаписьJSON;
    ЗаписьJSON.УстановитьСтроку();
    ЗаписатьJSON(ЗаписьJSON, СтруктураПакет);
    СтрокаJSON = ЗаписьJSON.Закрыть();
    
    // 5. Подключаемся к Web-сервису и передаем строку
    // Параметр метода "ПринятьДанные" на Web-сервисе должен иметь тип "string" (xs:string)
    Попытка
        Определение = Новый WSОпределения("http://localhost/BaseReceiver/ws/obmen.1cws?wsdl", "Admin", "123");
        Прокси = Новый WSProxy(Определение, "WebServiceNamespace", "Obmen", "ObmenSoap");
        Прокси.Пользователь = "Admin";
        Прокси.Пароль = "123";
        
        Результат = Прокси.ПринятьДанные(СтрокаJSON);
        Возврат Результат;
    Исключение
        ЗаписьЖурналаРегистрации("ОбменДанными", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        ВызватьИсключение "Ошибка при передаче данных через Web-сервис!";
    КонецПопытки;
    
КонецФункции

Реализация на стороне базы-приемника (Получатель)

Теперь проанализируем, как должна выглядеть функция Web-сервиса на стороне принимающей базы. Web-сервер принимает строку, парсит ее обратно в структуру 1С и обрабатывает полученные данные (ищет элементы по GUID или создает новые).


// Функция Web-сервиса, опубликованная в базе-приемнике
// Входной параметр: ДанныеСтрокой (Тип XDTO: string)
Функция ПринятьДанные(ДанныеСтрокой) Экспорт
    
    // 1. Десериализуем строку JSON обратно в объекты 1С
    ЧтениеJSON = Новый ЧтениеJSON;
    ЧтениеJSON.УстановитьСтроку(ДанныеСтрокой);
    
    Попытка
        ПолученнаяСтруктура = ПрочитатьJSON(ЧтениеJSON);
    Исключение
        ЧтениеJSON.Закрыть();
        Возврат "Ошибка парсинга JSON: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
    ЧтениеJSON.Закрыть();
    
    // 2. Воссоздаем локальную ТаблицуЗначений для удобства обработки
    ТаблицаРегионов = Новый ТаблицаЗначений;
    ТаблицаРегионов.Колонки.Добавить("Код", Новый ОписаниеТипов("Строка"));
    ТаблицаРегионов.Колонки.Добавить("Наименование", Новый ОписаниеТипов("Строка"));
    ТаблицаРегионов.Колонки.Добавить("КодРегиона", Новый ОписаниеТипов("Число"));
    ТаблицаРегионов.Колонки.Добавить("Ссылка", Новый ОписаниеТипов("СправочникСсылка.Регионы"));
    
    МассивДанных = ПолученнаяСтруктура.ДанныеТаблицы;
    
    НачатьТранзакцию();
    Попытка
        Для Каждого ЭлементИзМассива Из МассивДанных Цикл
            
            // Пытаемся найти существующий элемент по UID или по Коду
            УИД = Новый УникальныйИдентификатор(ЭлементИзМассива.UID);
            НайденнаяСсылка = Справочники.Регионы.ПолучитьСсылку(УИД);
            
            // Проверяем, существует ли реальный объект в базе данных
            Если ОбщегоНазначения.СсылкаСуществует(НайденнаяСсылка) Тогда
                ОбъектРегион = НайденнаяСсылка.ПолучитьОбъект();
            Иначе
                // Если не найден по UID, пробуем найти по бизнес-коду
                СсылкаПоКоду = Справочники.Регионы.НайтиПоКоду(ЭлементИзМассива.Код);
                Если ЗначениеЗаполнено(СсылкаПоКоду) Тогда
                    ОбъектРегион = СсылкаПоКоду.ПолучитьОбъект();
                Иначе
                    // Если объект совсем не найден, создаем новый с переданным UID
                    ОбъектРегион = Справочники.Регионы.СоздатьЭлемент();
                    ОбъектРегион.УстановитьСсылкуНового(НайденнаяСсылка);
                КонецЕсли;
            КонецЕсли;
            
            // Заполняем реквизиты данными
            ОбъектРегион.Код = ЭлементИзМассива.Код;
            ОбъектРегион.Наименование = ЭлементИзМассива.Наименование;
            ОбъектРегион.КодРегиона = ЭлементИзМассива.КодРегиона;
            
            ОбъектРегион.Записать();
            
        КонецЦикла;
        
        ЗафиксироватьТранзакцию();
    Исключение
        ОтменитьТранзакцию();
        ЗаписьЖурналаРегистрации("ОбменДанными", УровеньЖурналаРегистрации.Ошибка,,, ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
        Возврат "Ошибка при записи данных в базу-приемник: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;
    
    Возврат "Успешно обработано строк: " + МассивДанных.Количество();
    
КонецФункции

Альтернативное решение: Использование ХранилищаЗначения и кодирования Base64

Существует еще один надежный способ передачи произвольных данных. Он подходит в тех случаях, когда конфигурации баз идентичны и вы уверены, что типы метаданных совпадают. Этот метод базируется на помещении структуры или таблицы в системный объект ХранилищеЗначения с последующим кодированием в строку Base64.

Проанализируем пример кода упаковки данных на стороне отправителя:


// Упаковываем сложную структуру в Base64-строку
Хранилище = Новый ХранилищеЗначения(СтруктураПакет, Новый СжатиеДанных(9));
ДвоичныеДанные = Хранилище.Получить();
СтрокаBase64 = ПолучитьСтрокуBase64ИзДвоичныхДанных(ДвоичныеДанные);

// Передаем полученную СтрокуBase64 через параметр Web-сервиса
Результат = Прокси.ПринятьДанныеBase64(СтрокаBase64);

Разберем обратную процедуру распаковки на стороне получателя:


// Принимаем Base64-строку и восстанавливаем исходный объект
ДвоичныеДанные = ПолучитьДвоичныеДанныеИзСтрокиBase64(СтрокаBase64);
Хранилище = Новый ХранилищеЗначения(ДвоичныеДанные);
ВосстановленнаяСтруктура = Хранилище.Получить(); // Получили исходную структуру

Важное предостережение: Метод с ХранилищеЗначения привлекателен тем, что вам не нужно вручную преобразовывать таблицы значений в массивы структур. Платформа 1С берет это на себя при сжатии данных. Однако помните, что если в таблице значений содержатся ссылки на элементы справочников, которых нет в базе-приемнике, то после распаковки вы получите битые ссылки. Поэтому даже при данном подходе рекомендуется предварительно очищать передаваемую таблицу от прямых ссылок, заменяя их строковыми реквизитами.

Рекомендация: Переход на HTTP-сервисы

В современной разработке на платформе 1С:Предприятие для интеграции баз все чаще используются HTTP-сервисы вместо классических Web-сервисов (SOAP). Выделим ключевые преимущества такого перехода:

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

← На главную