Работа с XML-файлами — одна из самых частых задач при интеграции 1С с внешними системами. Механизм XML Data Transfer Objects (XDTO) является мощным и быстрым инструментом для решения этой задачи. Однако у начинающих разработчиков он часто вызывает трудности. Ошибки вроде «Структура объекта не соответствует типу...» могут поставить в тупик на несколько дней.
В этой статье мы подробно разберем, как устроен механизм XDTO, почему важно строго соблюдать иерархию XML-файла в схеме конфигуратора, как правильно читать списки объектов и как избежать подводных камней, связанных с пространствами имен.
Представим типичную задачу. Нам необходимо прочитать XML-файл, содержащий список клиентов, и загрузить их в справочник 1С. Наш XML-файл имеет следующую структуру:
<catalog>
<clients>
<client>Иван Иванов</client>
<client>Петр Петров</client>
</clients>
</catalog>
Типичная ошибка новичка — создать в пакете XDTO сразу тип объекта client и попытаться прочитать файл, указав этот тип в качестве ожидаемого. При попытке выполнить код платформа 1С выдаст ошибку: «Ошибка проверки данных XDTO: Структура объекта не соответствует типу...».
Выясним причину этой ошибки. Механизм XDTO не умеет самостоятельно «искать» нужные теги внутри XML-файла в обход его структуры. Чтение XML с использованием конкретного типа XDTO требует, чтобы корневой тег XML-файла строго соответствовал переданному типу. Поскольку наш корневой тег — это <catalog>, то и в схеме XDTO мы обязаны описать всю иерархию, начиная с самого верхнего уровня.
Чтобы успешно прочитать наш файл, нам нужно полностью воссоздать структуру вложенности тегов в пакете XDTO. Давайте разберем по шагам, как настроить типы в конфигураторе 1С:
catalog. Внутри него создадим свойство clients.clients. Внутри него создадим свойство client.client укажем тип данных. Если это просто текст (как в нашем примере), мы можем указать простой тип string из стандартного пространства имен XML-схемы (http://www.w3.org/2001/XMLSchema). Если же внутри тега <client> содержатся другие теги (например, <id> или <phone>), нам потребуется создать отдельный тип объекта client со своими свойствами.Важнейшая деталь: поскольку клиентов в списке может быть несколько, нам нужно явно указать платформе, что свойство client является коллекцией (массивом). Для этого в свойствах client (внутри типа clients) установим следующие параметры:
0-1 (что в терминах XML означает «не ограничено» или unbounded).Несоответствие пространств имен — это вторая по популярности причина ошибок при валидации XDTO. Проанализируем правила сопоставления URI:
<catalog xmlns="https://www.myproject.ru/xdto">. В этом случае в свойствах вашего пакета XDTO поле «URI пространства имен» должно содержать точно такую же строчку (с учетом регистра символов и слэшей).<catalog>. В этой ситуации поле «URI пространства имен» в свойствах вашего пакета XDTO в конфигураторе обязательно должно быть пустым! Если вы укажете там произвольный URI, 1С не сможет сопоставить файл со схемой.Помните также, что механизм XDTO крайне чувствителен к регистру. Тег <Client> в XML и свойство client в схеме будут распознаны как абсолютно разные элементы, что приведет к ошибке чтения.
После того как схема настроена, напишем код для чтения файла и получения коллекции клиентов. Разберем этот процесс на практическом примере:
// Укажем URI пространства имен нашего пакета (если оно задано в пакете)
АдресПространстваИмен = "https://www.myproject.ru/xdto";
// Получаем тип самого верхнего (корневого) элемента из фабрики XDTO
ТипФабрикиXDTO = ФабрикаXDTO.Тип(АдресПространстваИмен, "catalog");
// Инициализируем чтение XML-файла
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл("C:\Обмен\clients.xml");
Попытка
// Читаем весь XML-документ, приводя его к нашему корневому типу catalog
КаталогДанных = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML, ТипФабрикиXDTO);
// Обращаемся по цепочке свойств к коллекции клиентов
СписокКлиентов = КаталогДанных.clients.client;
// Переменная "СписокКлиентов" теперь имеет тип СписокXDTO.
// Обойдем ее в стандартном цикле 1С
Для Каждого ТекущийКлиент Из СписокКлиентов Цикл
// Обрабатываем каждого клиента. В нашем случае это просто строка
ИмяКлиента = ТекущийКлиент;
Сообщить("Прочитан клиент: " + ИмяКлиента);
// Здесь можно написать код для поиска или создания элемента справочника
КонецЦикла;
Исключение
Сообщить("Ошибка при чтении XML: " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
ЧтениеXML.Закрыть();
Рассмотрим важную особенность платформы 1С. В веб-разработке часто встречается ситуация, когда при конвертации XML в JSON один элемент возвращается как объект, а несколько — как массив. Это заставляет разработчиков писать дополнительные проверки на тип данных.
В XDTO всё устроено гораздо строже и удобнее. Если в вашей схеме для свойства client максимальное количество элементов указано как -1, то свойство КаталогДанных.clients.client всегда вернет объект типа СписокXDTO. Это произойдет даже в том случае, если в XML-файле физически присутствует только один клиент или тег вообще пуст. Такой подход гарантирует стабильность вашего кода и защищает его от неожиданных падений.
При вызове метода ФабрикаXDTO.ПрочитатьXML() мы передавали вторым параметром тип корневого элемента. Однако этот параметр можно сделать необязательным, упростив код. Для этого используется механизм глобальных элементов схемы:
catalog.catalog.После этого вы сможете читать XML-файлы одной простой строчкой, не определяя предварительно тип фабрики вручную:
КаталогДанных = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);
Платформа 1С самостоятельно проанализирует имя корневого тега в XML, найдет соответствующий глобальный элемент в пакетах XDTO, сопоставит пространство имен и автоматически вернет готовый объект нужного типа.
Если структура вашего XML-файла огромна (десятки вложенных тегов, адреса, реквизиты, товары), создавать схему вручную в конфигураторе крайне трудозатратно и высок риск ошибиться. Этот процесс можно легко автоматизировать:
.xsd..xsd.Платформа 1С мгновенно создаст готовый пакет XDTO со всеми необходимыми иерархическими связями, типами данных, правильными ограничениями количества элементов и пространствами имен. Вам останется только написать лаконичный код для чтения данных.