При разделении информационных баз или внесении изменений в типовые конфигурации программисты часто сталкиваются с ситуацией, когда структура метаданных в источнике и приемнике начинает различаться. Стандартный механизм СериализаторXDTO при попытке прочитать объект, содержащий "лишние" реквизиты, выдает ошибку, так как не находит соответствующих полей в базе-приемнике. В этой статье мы подробно разберем, как обойти это ограничение и реализовать гибкий обмен данными.
Рассмотрим стандартный подход, который часто приводит к ошибке. Если мы используем метод Выбрать(), система получает объект целиком со всеми его свойствами, включая те, которые были добавлены вручную. При попытке сериализации такого объекта в XML попадают все данные.
Проанализируем пример кода, вызывающий затруднения:
Запись = Новый ЗаписьXML;
Запись.УстановитьСтроку;
Запись.ЗаписатьОбъявлениеXML;
Запись.ЗаписатьНачалоЭлемента("Данные");
Выборка = Справочники.ФизическиеЛица.Выбрать;
Пока Выборка.Следующий() Цикл
// Здесь записывается весь объект со всеми доп. реквизитами
СериализаторXDTO.ЗаписатьXML(Запись, Выборка.ПолучитьОбъект);
КонецЦикла;
Запись.ЗаписатьКонецЭлемента;
Текст = Запись.Закрыть;
При чтении в другой базе метод СериализаторXDTO.ПрочитатьXML(Чтение) прервет работу, как только встретит узел, отсутствующий в метаданных. Разберем основные способы решения этой задачи.
Это самый правильный и производительный метод. Вместо того чтобы выгружать объект целиком, мы выберем только те поля, которые гарантированно присутствуют в обеих базах. Выясним, как это реализовать. Вместо Справочники.ФизическиеЛица.Выбрать() создадим запрос к таблице справочника.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ФизЛица.Код КАК Код,
| ФизЛица.Наименование КАК Наименование,
| ФизЛица.ДатаРождения КАК ДатаРождения
|ИЗ
| Справочник.ФизическиеЛица КАК ФизЛица";
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Пока Выборка.Следующий() Цикл
// Создаем структуру для передачи только нужных данных
ДанныеОбъекта = Новый Структура("Код, Наименование, ДатаРождения");
ЗаполнитьЗначенияСвойств(ДанныеОбъекта, Выборка);
// Сериализуем структуру, она более универсальна
СериализаторXDTO.ЗаписатьXML(Запись, ДанныеОбъекта);
КонецЦикла;
Преимущество этого метода: мы полностью контролируем состав передаваемых данных и не зависим от изменений в структуре объекта источника.
Если нам необходимо сохранить иерархию XML, идентичную стандартной сериализации объекта, но исключить конкретные реквизиты, мы можем воспользоваться методами объекта ЗаписьXML. Посмотрим на пример реализации, где каждый реквизит записывается отдельно:
ЗаписьXML.ЗаписатьНачалоЭлемента("CatalogObject.ФизическиеЛица");
// Записываем только стандартные реквизиты
ЗаписьXML.ЗаписатьНачалоЭлемента("Наименование");
ЗаписьXML.ЗаписатьТекст(Объект.Наименование);
ЗаписьXML.ЗаписатьКонецЭлемента();
ЗаписьXML.ЗаписатьНачалоЭлемента("Код");
ЗаписьXML.ЗаписатьТекст(Объект.Код);
ЗаписьXML.ЗаписатьКонецЭлемента();
// Пропускаем самописные реквизиты
ЗаписьXML.ЗаписатьКонецЭлемента();
Этот способ требует написания большего объема кода, но позволяет детально настроить структуру файла.
Иногда требуется быстрое решение без изменения логики выборки данных. В этом случае можно получить XML-представление объекта в виде строки, удалить из нее лишние теги с помощью строковых функций или регулярных выражений, и только после этого записывать в итоговый файл.
// Получаем XML одного объекта
ЗаписьОдного = Новый ЗаписьXML;
ЗаписьОдного.УстановитьСтроку();
СериализаторXDTO.ЗаписатьXML(ЗаписьОдного, Выборка.ПолучитьОбъект());
СтрXML = ЗаписьОдного.Закрыть();
// Пример удаления лишнего реквизита "ДополнительныйПараметр"
СтрXML = СтрЗаменить(СтрXML, "<ДополнительныйПараметр>", "");
// Здесь должна быть логика удаления содержимого и закрывающего тега
ЗаписьИтоговая.ЗаписатьТекст(СтрXML);
Важно: этот метод крайне не рекомендуется для постоянного использования, так как он медленный и подвержен ошибкам при изменении названий реквизитов.
Если обмен данными планируется на регулярной основе, рассмотрим вариант использования конфигурации "Конвертация данных". Это профессиональный инструмент, который специально предназначен для решения проблем несовпадения структур.
Если мы не можем изменить формат выгрузки, нам придется доработать логику чтения в базе-приемнике. Вместо автоматического чтения всего объекта через XDTO, мы проанализируем каждый узел вручную.
Разберем алгоритм:
ЧтениеXML.Прочитать().ЧтениеXML.Пропустить(), чтобы перейти к следующему узлу, не вызывая ошибку.
Пока Чтение.Прочитать() Цикл
Если Чтение.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
ИмяУзла = Чтение.ЛокальноеИмя;
Если Метаданные.Справочники.ФизическиеЛица.Реквизиты.Найти(ИмяУзла) <> Неопределено Тогда
// Читаем данные
Иначе
// Игнорируем неизвестный реквизит
Чтение.Пропустить();
КонецЕсли;
КонецЕсли;
КонецЦикла;
Подводя итог, можно сказать, что выбор метода зависит от конкретной задачи. Для разового переноса данных проще всего воспользоваться Запросом с четким перечислением полей. Если же требуется сложный обмен с трансформацией данных, лучшим выбором будет использование Конвертации данных. Помните, что использование СериализаторXDTO напрямую удобно только при полной идентичности конфигураций.