При интеграции 1С с внешними системами (например, ЕГИССО или СМЭВ) программисты часто сталкиваются с ситуацией, когда структура данных описывается не одним, а сразу несколькими взаимосвязанными файлами xsd. В таких схемах типы объектов могут переходить из одного пространства имен в другое через импорт. В этой статье мы разберем, как правильно инициализировать фабрику XDTO для такой структуры, как заполнять вложенные свойства и как избежать распространенных ошибок при записи готового объекта в файл XML.
Первая проблема, с которой можно столкнуться — это невозможность создать объект, тип которого определен в другом пакете. Если вы просто импортируете пакеты по одному в дерево конфигурации, 1С не всегда корректно связывает их на лету. Чтобы система видела все зависимости между 6 или более схемами, рассмотрим программную инициализацию фабрики.
Вместо использования стандартной глобальной ФабрикаXDTO, лучше создать собственную, объединив все схемы в один набор. Проанализируем следующий алгоритм:
НаборСхемXML..xsd).ФабрикаXDTO.
// Создаем набор схем и загружаем файлы
НаборСхем = Новый НаборСхемXML;
НаборСхем.Добавить("C:\Schemas\CommonTypes.xsd");
НаборСхем.Добавить("C:\Schemas\PersonInfo.xsd");
НаборСхем.Добавить("C:\Schemas\MainMessage.xsd");
// Инициализируем фабрику со всеми зависимостями
МояФабрика = Новый ФабрикаXDTO(НаборСхем);
Именно такой подход гарантирует, что при обращении к типу из одного пространства имен, который ссылается на тип в другом пространстве, 1С не выдаст ошибку "Тип не найден".
Разберем ситуацию, описанную в начале темы. При вызове метода ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, dataпа) возникает ошибка. Выясним причину: метод ЗаписатьXML ожидает, что передаваемый объект XDTO имеет четко определенное "локальное имя" (имя XML-тега). Однако, если мы создаем объект просто по типу через МояФабрика.Создать(Тип), объект получается "анонимным" — у него есть структура, но нет имени корневого элемента.
Чтобы решить эту проблему, необходимо в методе записи явно указать имя тега в третьем параметре. Рассмотрим исправленный код:
// Получаем тип корневого элемента
ТипКорня = МояФабрика.Тип("urn://egisso-ru/msg/10.06.S/1.0.2", "data");
dataпа = МояФабрика.Создать(ТипКорня);
// При записи ОБЯЗАТЕЛЬНО указываем имя тега (3-й параметр)
// Можно также указать префикс и пространство имен
ФабрикаXDTO.ЗаписатьXML(ЗаписьXML, dataпа, "data", "urn://egisso-ru/msg/10.06.S/1.0.2");
Важный момент: Если вы укажете "вася" вместо правильного имени (как в совете из темы), XML запишется, но внешняя система его не примет, так как схема ожидает конкретное имя узла, например data или package.
Посмотрим на ситуацию, когда свойство объекта ссылается на тип из другого пакета. Часто начинающие разработчики пытаются обращаться к свойствам по индексам (например, Свойства[1]). Это крайне ненадежно, так как при обновлении схемы порядок свойств может измениться. Разберем правильный способ заполнения через поиск типа по его URI.
Допустим, у нас есть объект "Персона", и нам нужно заполнить его фамилию, которая сама является сложным типом. Выполним следующие шаги:
// 1. Получаем тип самого объекта (например, данные о человеке)
ТипПерсоны = МояФабрика.Тип("urn://egisso-ru/types/prsn-info/1.0.3", "tPersonInfo");
ОбъектПерсона = МояФабрика.Создать(ТипПерсоны);
// 2. Получаем тип свойства из другого пакета (или этого же)
// Допустим, FamilyName — это сложный тип tName
ТипФамилии = МояФабрика.Тип("urn://egisso-ru/types/basic/1.0.1", "tName");
НоваяФамилия = МояФабрика.Создать(ТипФамилии);
НоваяФамилия.Value = "Иванов";
// 3. Присваиваем созданный объект свойству
ОбъектПерсона.FamilyName = НоваяФамилия;
Если свойство является списком (в схеме указано maxOccurs="unbounded"), то прямое присваивание вызовет ошибку. В этом случае нужно использовать метод ПолучитьСписок(). Проанализируем пример:
// Если у персоны может быть несколько документов
СписокДокументов = ОбъектПерсона.ПолучитьСписок("Documents");
ТипДокумента = МояФабрика.Тип("urn://egisso-ru/types/docs/1.0.0", "tDocument");
НовыйДокумент = МояФабрика.Создать(ТипДокумента);
НовыйДокумент.Series = "1234";
НовыйДокумент.Number = "567890";
// Добавляем объект в коллекцию
СписокДокументов.Добавить(НовыйДокумент);
Для корректной работы со сложными государственными информационными системами важно, чтобы XML-файл содержал правильные префиксы (tns, ns1 и т.д.). Рассмотрим, как принудительно назначить соответствие префиксов перед выгрузкой.
Перед тем как вызвать ЗаписатьXML, воспользуемся методами объекта ЗаписьXML:
ЗаписьXML = Новый ЗаписьXML;
ЗаписьXML.ОткрытьФайл("C:\temp\export.xml");
// Явно задаем префиксы, которые ожидает приемник
ЗаписьXML.ЗаписатьСоответствиеПространствИмен("msg", "urn://egisso-ru/msg/10.06.S/1.0.2");
ЗаписьXML.ЗаписатьСоответствиеПространствИмен("prsn", "urn://egisso-ru/types/prsn-info/1.0.3");
// Теперь записываем данные
МояФабрика.ЗаписатьXML(ЗаписьXML, dataпа, "data", "urn://egisso-ru/msg/10.06.S/1.0.2");
ЗаписьXML.Закрыть();
Чтобы не гадать, почему внешняя система отклоняет файл, воспользуемся встроенным методом проверки 1С. Перед записью объекта в файл рекомендуется вызвать метод Проверить(). Он проанализирует все 6 пакетов данных на предмет обязательности полей и соответствия типов.
Попытка
dataпа.Проверить();
Сообщить("Объект XDTO заполнен корректно!");
Исключение
Сообщить("Ошибка валидации: " + ОписаниеОшибки());
КонецПопытки;
Подведем итог: Работа с несколькими пакетами XDTO требует внимательного отношения к пространствам имен. Основной секрет успеха заключается в использовании НаборСхемXML для инициализации фабрики и обязательном указании локального имени тега при выгрузке в XML. Используйте создание объектов по типам через МояФабрика.Тип(), и тогда структура любой сложности будет заполнена без ошибок.