Перенос данных между различными конфигурациями 1С с использованием механизмов XDTO — задача стандартная, но имеющая свои «подводные камни». Часто программисты сталкиваются с ситуацией, когда обычные справочники переносятся без проблем, а при попытке передать подчиненный справочник (например, Спецификации, подчиненные Номенклатуре) база-приемник выдает ошибку при чтении XML. Рассмотрим подробно, почему это происходит и как правильно сформировать объект XDTO, чтобы поле Owner (Владелец) корректно десериализовалось.
Проанализируем типичную ситуацию. Разработчик создает объект XDTO вручную, используя фабрику, созданную на основе внешней XSD-схемы. Код в базе-источнике может выглядеть следующим образом:
СпецификацияТип = Фабрика.Тип("http://v8.1c.ru/8.1/data/enterprise/current-config", "CatalogObject.Спецификации");
СпецификацияОбъект = Фабрика.Создать(СпецификацияТип);
СпецификацияОбъект.Code = Выборка.Ссылка.Код;
СпецификацияОбъект.Description = Выборка.Ссылка.Наименование;
// Ошибка закрадывается здесь:
СпецификацияОбъект.Owner = XMLСтрока(Выборка.Владелец);
СпецификацияОбъект.Ref = XMLСтрока(Выборка.Ссылка);
Фабрика.ЗаписатьXML(ЗаписьХМЛ, СпецификацияОбъект);
При попытке прочитать полученный XML в базе-приемнике через СериализаторXDTO.ПрочитатьXML(ЧтениеХМЛ) возникает ошибка. Проблема заключается в том, что в схеме 1С поле Owner имеет тип ent:AnyRef. Это полиморфный тип, который может содержать ссылку на любой справочник. Когда мы присваиваем этому полю просто строку через XMLСтрока(Выборка.Владелец), фабрика записывает в XML чистый UUID без указания того, к какому типу метаданных он относится.
Выясним причину сбоя: база-приемник получает UUID, но не понимает, в какой таблице искать этого владельца, так как отсутствует атрибут xsi:type. Без него десериализатор не может создать объект ссылки нужного типа.
Для того чтобы в результирующем XML-файле появился необходимый атрибут xsi:type="CatalogRef.Номенклатура", мы должны передать в свойство Owner не строку, а специальный объект XDTO, представляющий ссылку. Разберем этот процесс по шагам.
Посмотрим на пример исправленного кода:
// Получаем тип ссылки конкретного справочника-владельца из пространства имен enterprise
ТипВладельца = Фабрика.Тип("http://v8.1c.ru/8.1/data/enterprise/current-config", "CatalogRef.Номенклатура");
// Создаем объект ссылки XDTO
ОбъектВладелец = Фабрика.Создать(ТипВладельца, XMLСтрока(Выборка.Владелец));
// Теперь присваиваем свойству Owner не строку, а объект
СпецификацияОбъект.Owner = ОбъектВладелец;
В этом случае при вызове метода Фабрика.ЗаписатьXML система увидит, что тип значения является расширением абстрактного AnyRef, и автоматически добавит в XML-тег Owner описание типа. База-приемник успешно распознает тип и создаст ссылку.
Если конфигурации базы-источника и базы-приемника позволяют, можно упростить процесс типизации, комбинируя возможности глобального сериализатора и вашей кастомной фабрики. Проанализируем ситуацию: СериализаторXDTO умеет правильно преобразовывать объекты 1С в объекты XDTO со всеми необходимыми атрибутами типов.
Рассмотрим вариант, когда мы сначала получаем готовый XDTO-объект из ссылки, а затем используем его данные:
// Получаем стандартный XDTO объект ссылки через глобальный сериализатор
СсылкаXDTO = СериализаторXDTO.ВXDTOОбъект(Выборка.Владелец);
// Присваиваем этот объект в поле Owner нашего вручную собираемого справочника
СпецификацияОбъект.Owner = СсылкаXDTO;
Важный момент: этот способ сработает только в том случае, если пространство имен (Namespace) в вашей фабрике совпадает с тем, которое использует СериализаторXDTO (обычно это http://v8.1c.ru/8.1/data/enterprise/current-config).
Давайте сравним «неправильный» и «правильный» XML, чтобы понимать, чего именно мы добиваемся.
Неправильный XML (ошибка десериализации):
<Owner>d37722b4-6cd9-11e9-93af-8c16454e3d70</Owner>
Здесь десериализатор видит строку, но поле Owner ожидает сложный тип AnyRef. Происходит конфликт типов.
Правильный XML (успешная загрузка):
<Owner xsi:type="v8e:CatalogRef.Номенклатура" xmlns:v8e="http://v8.1c.ru/8.1/data/enterprise/current-config">d37722b4-6cd9-11e9-93af-8c16454e3d70</Owner>
Благодаря атрибуту xsi:type, механизм XDTO точно знает, что данную строку нужно интерпретировать как ссылку на справочник Номенклатура.
При разработке сложных обменов рекомендуем придерживаться следующих правил:
AnyRef. Если вы видите этот тип, значит, при ручном заполнении свойства через фабрику вам обязательно нужно создавать объект ссылки, а не передавать строку.объ.Записать() может вызвать исключение нарушения целостности данных.ПрочитатьXML у СериализаторXDTO только тогда, когда вы уверены, что структура XML полностью соответствует типам метаданных в приемнике. Если структуры разные, читайте XML во временный XDTO-объект, а затем вручную заполняйте реквизиты справочника 1С.Таким образом, мы выяснили, что секрет успешного переноса подчиненных справочников через XDTO заключается в правильной обработке полиморфных полей. Передавая в свойство Owner полноценный объект XDTO вместо строкового UUID, вы обеспечиваете корректную передачу метаинформации о типе ссылки, что позволяет стандартным механизмам 1С беспрепятственно выполнять загрузку данных.