При интеграции систем через XML-документы разработчики на платформе 1С часто используют технологии XDTO (XML Data Transfer Objects). Этот механизм позволяет прозрачно работать со сложными структурами данных, валидировать их по схемам XSD и сериализовать в XML-формат. Однако при работе со специфическими типами данных возникает неприятная проблема: заполненное свойство __content упорно не проходит проверку встроенным методом Проверить(), генерируя ошибку о том, что обязательное свойство отсутствует. При этом в табло отладчика свойство выглядит абсолютно корректно заполненным.
В этой статье мы подробно разберем, почему возникает такая ситуация, как платформа 1С обрабатывает текстовое содержимое элементов с атрибутами, почему происходит конфликт пространств имен и как раз и навсегда решить эту проблему с помощью проверенных практических методов.
Для начала давайте разберем, откуда вообще берется свойство с двойным подчеркиванием __content. В XML-схемах (XSD) элементы могут определяться по-разному. Довольно часто встречается структура, когда элемент должен содержать текстовое значение (простой тип), но в то же время иметь несколько атрибутов. В терминах XSD такая структура описывается как сложный тип с простым содержимым (complexType с расширением simpleContent).
Рассмотрим пример такого XML-элемента:
<Account bic="044525225" name="ПАО Сбербанк">40702810938000001234</Account>
Здесь bic и name выступают в качестве атрибутов XML-тега, а банковский счет 40702810938000001234 — это непосредственное текстовое тело элемента. Платформа 1С при импорте такой схемы XSD не может представить текстовое значение как обычное свойство объекта XDTO, поскольку у объекта уже есть свойства-атрибуты. Чтобы решить эту коллизию, платформа автоматически генерирует системное скрытое свойство с зарезервированным именем __content. Именно в него записывается текстовая составляющая XML-элемента.
При заполнении такого объекта разработчики обычно пишут следующий код:
НовыйОбъект = ФабрикаXDTO.Создать(ТипОбъекта);
НовыйОбъект.bic = "044525225";
НовыйОбъект.name = "ПАО Сбербанк";
НовыйОбъект.__content = "40702810938000001234";
Казалось бы, все сделано правильно. Но при вызове метода НовыйОбъект.Проверить() платформа выбрасывает исключение: "Проверка свойства '__content': отсутствует обязательное свойство". Выясним, почему так происходит.
Главная и самая неочевидная причина кроется в архитектуре платформы 1С:Предприятие при работе с фабриками XDTO. Давайте проанализируем поведение платформы по шагам:
http://www.cbr.ru/projects/formats.СоздатьФабрикуXDTO(ПутьКФайлуСхемы).targetNamespace), что и один из встроенных в конфигурацию XDTO-пакетов.Если встроенный в конфигурацию XDTO-пакет устарел, был изменен или изначально не содержал корректного описания типа simpleContent для нужного нам свойства, созданный объект XDTO фактически инициализируется по старой схеме. В этой старой схеме свойство __content может не существовать в качестве текстового заполнения или иметь другой тип данных. В результате, несмотря на то что отладчик позволяет нам записать значение в __content динамически, внутренний валидатор платформы считает структуру объекта некорректной и бракует его при проверке.
Самый надежный способ обойти данное ограничение платформы — сделать пространство имен загружаемой схемы уникальным в рамках текущего сеанса работы. Поскольку уникальность схем в фабрике XDTO жестко привязана к строке URI пространства имен, мы можем программно изменить его перед созданием фабрики, а затем вернуть исходное значение на этапе формирования финального XML-документа.
Давайте подробно разберем этот алгоритм по шагам:
СтрЗаменить() заменим оригинальное пространство имен на временное (например, добавив к нему суффикс /temp). Замену нужно произвести как в объявлении targetNamespace, так и во всех импортах и префиксах внутри схемы.Проверить() пройдет успешно.Посмотрим на практический пример реализации этого метода в коде 1С:
// Шаг 1. Получаем текст исходной схемы XSD (например, из макета)
ТекстСхемы = ПолучитьМакет("ИмпортируемаяСхемаXSD").ПолучитьТекст();
ОригинальноеПространство = "http://www.cbr.ru/projects/formats";
ВременноеПространство = "http://www.cbr.ru/projects/formats/temp";
// Шаг 2. Подменяем пространство имен в тексте схемы
МодифицированныйТекстСхемы = СтрЗаменить(ТекстСхемы, ОригинальноеПространство, ВременноеПространство);
// Шаг 3. Записываем временную схему на диск
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xsd");
ТекстовыйДокумент = Новый ТекстовыйДокумент;
ТекстовыйДокумент.УстановитьТекст(МодифицированныйТекстСхемы);
ТекстовыйДокумент.Записать(ИмяВременногоФайла, КодировкаТекста.UTF8);
// Шаг 4. Создаем фабрику XDTO на основе модифицированной схемы
МассивФайлов = Новый Массив;
МассивФайлов.Добавить(ИмяВременногоФайла);
ВременнаяФабрика = СоздатьФабрикуXDTO(МассивФайлов);
// Удаляем временный файл с диска
Попытка
УдалитьФайлы(ИмяВременногоФайла);
Исключение
// Обработка возможных ошибок блокировки файла
КонецПопытки;
// Шаг 5. Работаем с объектом XDTO через новую фабрику
ТипАккаунт = ВременнаяФабрика.Тип(ВременноеПространство, "AccNumType");
ОбъектAccount = ВременнаяФабрика.Создать(ТипАккаунт);
ОбъектAccount.bic = "044525225";
ОбъектAccount.name = "ПАО Сбербанк";
ОбъектAccount.__content = "40702810938000001234";
// Проверяем объект - теперь ошибки не возникнет!
Если Не ОбъектAccount.Проверить() Тогда
ВызватьИсключение "Ошибка валидации объекта XDTO!";
КонецЕсли;
// Шаг 6. Сериализуем объект в XML-строку
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.УстановитьСтроку();
ВременнаяФабрика.ЗаписатьXML(ЗаписьXML, ОбъектAccount, "Account", ВременноеПространство);
ВременнаяXMLСтрока = ЗаписьXML.Закрыть();
// Шаг 7. Возвращаем оригинальное пространство имен в XML-документ
ФинальныйXML = СтрЗаменить(ВременнаяXMLСтрока, ВременноеПространство, ОригинальноеПространство);
Этот способ является максимально надежным, так как он полностью изолирует динамически загружаемую схему от метаданных конфигурации 1С и гарантирует предсказуемое поведение платформы при выполнении валидации.
В некоторых случаях структура элемента описывается в XSD не как simpleContent, а как тип со смешанным содержимым (mixed="true"). При такой структуре элемент может содержать одновременно и дочерние XML-теги, и произвольный текст между ними.
Для таких типов прямое присвоение значения свойству __content может не поддерживаться платформой 1С на программном уровне. В этом случае для записи текстового значения нам необходимо использовать специальный объект платформы — ПоследовательностьXDTO.
Проанализируем ситуацию и рассмотрим пример кода, который позволяет обойти проблему валидации через добавление текста в последовательность:
ТипОбъекта = ФабрикаXDTO.Тип(ПространствоИмен, "ComplexMixedType");
НовыйОбъект = ФабрикаXDTO.Создать(ТипОбъекта);
// Заполняем обычные атрибуты или дочерние свойства
НовыйОбъект.SomeAttribute = "ЗначениеАтрибута";
// Работаем с последовательностью для заполнения текстового содержимого
Если НовыйОбъект.Последовательность() <> Неопределено Тогда
// Добавляем текст непосредственно в последовательность XDTO
НовыйОбъект.Последовательность().ДобавитьТекст("Текстовое значение элемента");
КонецЕсли;
// Выполняем проверку заполнения
Если НовыйОбъект.Проверить() Тогда
// Объект успешно прошел валидацию
КонецЕсли;
Этот подход незаменим, когда схема XSD накладывает жесткие требования к порядку следования текстовых блоков и дочерних узлов XML-документа.
Если у вас есть возможность вносить изменения в метаданные разрабатываемого решения, самым правильным с точки зрения архитектуры решением будет устранение самого источника конфликта. Для этого выполните следующие действия:
После приведения встроенных метаданных в соответствие с актуальной схемой необходимость динамической замены пространств имен отпадет сама собой, и вы сможете использовать стандартные методы создания объектов через общую системную фабрику XDTO.
Используя предложенные методы, вы сможете легко решить проблему с валидацией свойства __content и обеспечить надежную и стабильную работу механизмов интеграции в ваших прикладных решениях на платформе 1С.