При тиражировании решений, разработке шаблонов настроек или выгрузке данных между разными версиями одной конфигурации разработчики часто сталкиваются с проблемой: структура метаданных источника и приемника не совпадает. Например, в новой версии справочника появились новые реквизиты или были удалены старые. Если попытаться загрузить данные штатными методами платформы, обмен завершается ошибкой.
Разберем, почему стандартные механизмы дают сбой и как реализовать надежный обмен данными без падений из-за различий в структуре объектов.
Проанализируем работу механизма СериализаторXDTO. При выполнении методов СериализаторXDTO.ПрочитатьXML() или ПрочитатьJSON() с передачей сериализатора платформа обращается к фабрике XDTO и фиксированному пакету текущей конфигурации (http://v8.1c.ru/8.1/data/enterprise/current-config).
Важное ограничение: платформа выполняет строгую валидацию структуры. Если во входящем файле присутствуют реквизиты, свойства или типы, которые отсутствуют в метаданных текущей базы-приемника (например, база-приемник более старая), десериализация завершается аварийно с исключением. Отключить эту проверку параметрами платформенных методов невозможно.
Второй аспект касается ссылочных типов. При прямой десериализации ссылки выгружаются в виде UUID. Если объект с таким идентификатором в приемнике отсутствует, образуется «битая ссылка» (объект не найден), если предварительно не реализован алгоритм сопоставления по естественным ключевым полям.
Рассмотрим наиболее гибкий и простой подход, предложенный в обсуждении: отказаться от жесткой типизации XDTO при загрузке и использовать промежуточные структуры платформы.
Разберем по шагам, как построить надежный процесс чтения и записи данных:
ЧтениеJSON и функцию ПрочитатьJSON() без передачи в параметры объекта СериализаторXDTO. В этом случае данные из файла трансформируются в универсальные коллекции платформы: Структура, Соответствие и Массив. Чтение никогда не прервется ошибкой из-за отсутствия полей.ЗаполнитьЗначенияСвойств(Приемник, Источник). Этот метод копирует только те свойства, имена которых совпадают у обоих объектов, полностью игнорируя лишние поля из файла.ЗаполнитьЗначенияСвойств не умеет автоматически сопоставлять и добавлять строки табличных частей. Обходим табличные части программно, проверяя их наличие в метаданных.Посмотрим на пример кода, реализующего безопасную загрузку элемента справочника из десериализованной структуры JSON:
// Предположим, что ДанныеЭлемента — это десериализованная Структура из JSON
ИмяСправочника = "ШаблоныНастроек";
МетаданныеОбъекта = Метаданные.Справочники.Найти(ИмяСправочника);
Если МетаданныеОбъекта <> Неопределено Тогда
// Ищем объект по ключевому реквизиту (например, Код)
СсылкаНаОбъект = Справочники[ИмяСправочника].НайтиПоКоду(ДанныеЭлемента.Код);
Если СсылкаНаОбъект.Пустая() Тогда
ОбъектБазы = Справочники[ИмяСправочника].СоздатьЭлемент();
Иначе
ОбъектБазы = СсылкаНаОбъект.ПолучитьОбъект();
КонецЕсли;
// Безопасно заполняем совпадающие реквизиты шапки
ЗаполнитьЗначенияСвойств(ОбъектБазы, ДанныеЭлемента);
// Обрабатываем табличные части
Если ДанныеЭлемента.Свойство("ТабличныеЧасти") Тогда
Для Каждого ОписаниеТЧ Из ДанныеЭлемента.ТабличныеЧасти Цикл
ИмяТЧ = ОписаниеТЧ.Ключ;
СтрокиТЧ = ОписаниеТЧ.Значение;
// Проверяем, существует ли такая ТЧ в текущей конфигурации
Если МетаданныеОбъекта.ТабличныеЧасти.Найти(ИмяТЧ) <> Неопределено Тогда
ТЧОбъекта = ОбъектБазы[ИмяТЧ];
ТЧОбъекта.Очистить();
Для Каждого СтрокаИсточника Из СтрокиТЧ Цикл
НоваяСтрока = ТЧОбъекта.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаИсточника);
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецЕсли;
ОбъектБазы.Записать();
КонецЕсли;
Рассмотрим ситуацию, когда имя реквизита в разных версиях совпадает, но разработчики изменили его допустимый тип (например, было Число, а стала Строка или ссылка). Чтобы избежать ошибки несовместимости типов при записи, выполним проверку через метаданные конфигурации.
Проверим доступность типа загружаемого значения:
РеквизитМетаданных = МетаданныеОбъекта.Реквизиты.Найти("ВерсияФормата");
Если РеквизитМетаданных <> Неопределено Тогда
ТипЗначенияИзФайла = ТипЗнч(ДанныеЭлемента.ВерсияФормата);
// Проверяем, поддерживает ли реквизит приемника тип загружаемого значения
Если РеквизитМетаданных.Тип.СодержитТип(ТипЗначенияИзФайла) Тогда
ОбъектБазы.ВерсияФормата = ДанныеЭлемента.ВерсияФормата;
КонецЕсли;
КонецЕсли;
Если проект требует более формализованного обмена или планируется постоянная синхронизация, рассмотрим альтернативные варианты:
minOccurs="0" или применяются типы anyType / anySimpleType. При изменении реквизитного состава в базах сам контракт обмена не нарушается, и методы XDTO продолжают работать без сбоев.Универсальный обмен данными в формате XML позволяет задействовать обработчики событий (ПередЗагрузкой, ПриЗагрузке), где программно анализируются метаданные приемника, а пользователь может вручную отобрать необходимые данные перед фактической записью.