При интеграции 1С с внешними Web-сервисами программисты часто сталкиваются с ситуацией, когда один из аргументов метода имеет универсальный тип anyType. Согласно спецификации XML, такой тип может содержать практически любые данные. Однако на практике сторонние сервисы часто требуют строгого соблюдения структуры: элемент должен не только содержать значение, но и иметь явный атрибут xsi:type, указывающий на тип данных (например, xsd:int или xsd:string).
Стандартные механизмы платформы 1С при работе с ФабрикаXDTO не всегда корректно формируют такую структуру автоматически, особенно если тип в WSDL-схеме определен как «смешанный» или требует ручного управления последовательностью. Разберем детально, как решить эту задачу несколькими способами.
Рассмотрим ситуацию, когда сервис ожидает XML следующего вида:
<arg2 xsi:type="xsd:int">0</arg2>
Попытка создать такой объект через ФабрикаXDTO.Создать() и последующее добавление текста в Последовательность() часто приводит к ошибке «Недостаточно фактических параметров». Это происходит из-за особенностей реализации метода Добавить() в объекте ПоследовательностьXDTO в старых релизах платформы (до 8.3.14). Система ожидает второй параметр, даже если вы добавляете простой текст.
Кроме того, если тип anyType в используемой схеме не помечен свойством Смешанный (Mixed) = Истина, работа с последовательностью и добавление текста будут заблокированы на уровне ядра XDTO.
Если ваша платформа ниже версии 8.3.14, или если вы хотите использовать наиболее «правильный» с точки зрения объектной модели путь, воспользуйтесь методом работы с последовательностью. Чтобы избежать ошибки количества параметров, необходимо передать Неопределено в качестве второго аргумента.
Разберем этот метод по шагам:
anyType через фабрику вашего WS-сервиса.xsi:type).Пример кода:
// Создаем тип anyType из стандартного пространства имен XML Schema
ТипAnyType = ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "anyType");
ОбъектXDTO = ФабрикаXDTO.Создать(ТипAnyType);
// Добавляем атрибут xsi:type вручную, если это необходимо
// Хотя зачастую 1С сама управляет этим при правильной типизации
ЗначениеТипа = ФабрикаXDTO.Создать(ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "string"), "xsd:integer");
ОбъектXDTO.Добавить(ФормаXML.Атрибут, "http://www.w3.org/2001/XMLSchema-instance", "type", ЗначениеТипа);
// Работаем с последовательностью для добавления текстового содержимого (#text)
ТекПоследовательность = ОбъектXDTO.Последовательность();
Если ТекПоследовательность <> Неопределено Тогда
// Важно: передаем Неопределено вторым параметром для совместимости
ТекПоследовательность.Добавить("777720109", Неопределено);
КонецЕсли;
Проанализируем более универсальный и надежный способ, который позволяет «обмануть» строгую валидацию фабрики при создании объекта. Этот метод заключается в ручном формировании нужного фрагмента XML в памяти и его последующем чтении в объект XDTO. Это гарантирует, что структура объекта будет в точности соответствовать вашим ожиданиям.
Посмотрим на реализацию этого алгоритма:
// 1. Подготавливаем поток в памяти для записи XML
Поток = Новый ПотокВПамяти();
Запись = Новый ЗаписьXML;
Запись.ОткрытьПоток(Поток);
// 2. Формируем структуру вручную
Запись.ЗаписатьОбъявлениеXML();
Запись.ЗаписатьНачалоЭлемента("anyType");
// Объявляем необходимые пространства имен
Запись.ЗаписатьАтрибут("xmlns", "http://www.w3.org/2001/XMLSchema");
Запись.ЗаписатьАтрибут("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
// Указываем тип через атрибут
Запись.ЗаписатьАтрибут("xsi:type", "xsd:int");
// Записываем само значение (текстовый узел)
Запись.ЗаписатьТекст("777720109");
Запись.ЗаписатьКонецЭлемента();
Запись.Закрыть();
// 3. Читаем сформированный XML обратно в объект XDTO
Поток.Перейти(0, ПозицияВПотоке.Начало);
Чтение = Новый ЧтениеXML;
Чтение.ОткрытьПоток(Поток);
// Используем ФабрикаXDTO.ПрочитатьXML для получения готового объекта
ОбъектРезультат = ФабрикаXDTO.ПрочитатьXML(Чтение);
Чтение.Закрыть();
Поток.Закрыть();
// Теперь ОбъектРезультат можно присвоить свойству другого XDTO-объекта
Преимущество этого подхода в том, что ЗаписьXML не накладывает жестких ограничений на структуру, в отличие от ФабрикаXDTO.Создать(). После того как объект «прочитан» из корректного XML, он сохраняет свою внутреннюю структуру и правильно сериализуется при отправке запроса к сервису.
Проанализируем ситуацию: если свойство имеет тип anyType, а нам нужно передать простое число, мы можем создать не XDTOОбъект, а XDTOЗначение. Платформа 1С при сериализации объектов XDTOЗначение в поля типа anyType часто автоматически добавляет атрибут xsi:type.
Пример использования XDTOЗначение:
// Получаем тип xsd:int
ТипЧисло = ФабрикаXDTO.Тип("http://www.w3.org/2001/XMLSchema", "int");
// Создаем именно значение, а не объект
ЗначениеXDTO = ФабрикаXDTO.Создать(ТипЧисло, 777720109);
// Присваиваем это значение свойству родительского объекта
// РодительскийОбъект.СвойствоТипаAnyType = ЗначениеXDTO;
В этом случае 1С увидит, что в поле «любого типа» передано конкретное типизированное значение, и при выгрузке в XML добавит xsi:type="xsd:int" самостоятельно. Это наиболее лаконичный способ, если он поддерживается конкретной реализацией вашего Web-сервиса.
При ручном добавлении атрибутов типа xsi:type крайне важно следить за префиксами пространств имен. В XML-документе префикс xsi должен быть привязан к URI http://www.w3.org/2001/XMLSchema-instance, а префикс xsd (или другой, используемый в значении типа) — к http://www.w3.org/2001/XMLSchema.
Если вы используете метод ОбъектXDTO.Добавить(), убедитесь, что вы передаете правильный URI пространства имен атрибута, иначе сервис может не распознать тип данных и вернуть ошибку валидации.
Подведем итог: для решения проблемы с anyType в старых версиях 1С используйте добавление в последовательность с параметром Неопределено. Для максимально гибкого контроля структуры используйте метод с промежуточным ПотокВПамяти и ЗаписьXML. А для простых типов данных сначала попробуйте создание XDTOЗначение с явным указанием типа из пространства имен XMLSchema.