Как восстановить версию объекта в 1С:УПП, если изменился состав реквизитов и ПрочитатьXML выдает ошибку?

Программист 1С v8.3 (Обычные формы)
← На главную

При работе с механизмами версионирования в конфигурации «Управление производственным предприятием» (УПП) разработчики часто сталкиваются с неприятной ситуацией: при попытке восстановить объект из истории изменений система выдает критическую ошибку. Чаще всего это происходит в строке ПрочитатьXML(ЧтениеXML). Проанализируем ситуацию: основная причина кроется в том, что с момента записи версии состав реквизитов объекта в конфигурации изменился — какие-то поля были удалены, переименованы или изменили свой тип данных.

Рассмотрим подробнее, почему стандартные средства платформы 1С пасуют перед этой задачей и как мы можем реализовать «мягкое» восстановление данных, игнорируя несоответствия в метаданных.

Почему возникает ошибка при использовании ПрочитатьXML?

Метод ПрочитатьXML предназначен для полной и строгой десериализации объекта. Платформа ожидает, что структура XML-файла идеально соответствует текущему описанию объекта в метаданных конфигурации. Если в XML встречается узел (реквизит), который больше не существует в коде программы, или если структура табличной части претерпела изменения, метод выбрасывает исключение. Игнорировать ошибки внутри этого метода невозможно — это заложено в логику работы платформенного движка.

Для решения этой проблемы нам необходимо отказаться от автоматического чтения и перейти к ручному управлению процессом заполнения данных. Разберем основные стратегии решения.

Способ 1: Реализация ручного парсинга через ЧтениеXML

Вместо того чтобы доверять чтение всей структуры платформе, мы можем самостоятельно перебирать узлы XML-файла и проверять их наличие в текущем объекте. Это самый надежный способ для старых конфигураций типа УПП.

Проанализируем алгоритм реализации:

  1. Создадим экземпляр нужного объекта по ссылке или создадим новый.
  2. Откроем файл XML через ЧтениеXML.
  3. В цикле будем читать каждый узел.
  4. Для каждого найденного имени узла будем проверять: существует ли такой реквизит в метаданных объекта.
  5. Если реквизит существует — заполняем его. Если нет — просто пропускаем узел.

Посмотрим на пример кода, который реализует такой подход:


Процедура ВосстановитьВерсиюВручную(ДанныеХранилища, СсылкаНаОбъект)
    // Получаем двоичные данные из хранилища версии
    ДвоичныеДанные = ДанныеХранилища.Получить();
    ИмяФайла = ПолучитьИмяВременногоФайла("xml");
    ДвоичныеДанные.Записать(ИмяФайла);

    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.ОткрытьФайл(ИмяФайла);

    // Создаем объект для записи
    ОбъектДляВосстановления = СсылкаНаОбъект.ПолучитьОбъект();
    МетаданныеОбъекта = ОбъектДляВосстановления.Метаданные();

    // Читаем до начала данных объекта
    Пока ЧтениеXML.Прочитать() Цикл
        ТипУзла = ЧтениеXML.ТипУзла;
        
        // Если это начало элемента и это не корневой элемент объекта
        Если ТипУзла = ТипУзлаЧтенияXML.НачалоЭлемента Тогда
            ИмяУзла = ЧтениеXML.ЛокальноеИмя;
            
            // Проверяем наличие обычного реквизита
            РеквизитМетаданных = МетаданныеОбъекта.Реквизиты.Найти(ИмяУзла);
            Если РеквизитМетаданных <> Неопределено Тогда
                // Считываем значение с учетом типа, определенного в метаданных
                ОбъектДляВосстановления[ИмяУзла] = ПрочитатьXML(ЧтениеXML, РеквизитМетаданных.Тип);
            ИначеЕсли МетаданныеОбъекта.ТабличныеЧасти.Найти(ИмяУзла) <> Неопределено Тогда
                // Здесь должна быть логика обхода строк табличной части
                ЗаполнитьТабличнуюЧастьВручную(ОбъектДляВосстановления[ИмяУзла], ЧтениеXML);
            Иначе
                // Если реквизит не найден, просто пропускаем содержимое узла
                ЧтениеXML.Пропустить();
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;

    ЧтениеXML.Закрыть();
    ОбъектДляВосстановления.Записать();
    УдалитьФайлы(ИмяФайла);
КонецПроцедуры

Способ 2: Использование объектной модели DOM

Если структура XML сложная, а ручной перебор через ЧтениеXML кажется слишком громоздким, можно воспользоваться Построителем DOM. Этот метод позволяет загрузить XML в дерево объектов в памяти, что значительно упрощает навигацию.

Рассмотрим преимущества этого подхода: мы можем обращаться к данным по именам тегов и не беспокоиться о порядке следования реквизитов в файле. Если в нашей версии объекта из истории есть лишние данные, мы их просто не вызовем при заполнении текущего объекта 1С.

Пример логики работы с DOM:

  1. Загружаем XML в ДокументDOM.
  2. Перебираем коллекцию Реквизиты из метаданных текущего объекта.
  3. Для каждого имени реквизита ищем соответствующий узел в дереве DOM методом ПолучитьЭлементыПоИмени().
  4. При нахождении — переносим значение в объект.

Способ 3: Подход из современной Библиотеки Стандартных Подсистем (БСП)

В новых версиях БСП разработчики 1С уже внедрили механизмы, устойчивые к изменению метаданных. Вместо прямого использования ПрочитатьXML, они применяют специализированные обертки. Выясним причину их эффективности: они используют формат FastInfoSet и защищенные процедуры десериализации.

Если в вашей конфигурации есть модули БСП, проверьте наличие функции ВосстановитьОбъектПоXML. Как правило, она обернута в блок Попытка...Исключение, но внутри содержит дополнительную логику сопоставления полей. В случае УПП, где БСП может быть сильно устаревшей, можно позаимствовать код из последних релизов БСП (например, 3.1), где реализован «умный» разбор XML-пакетов.

Важные рекомендации по работе с табличными частями

Самое сложное при ручном восстановлении — это табличные части. Посмотрим на нюансы, которые стоит учесть:

Таким образом, для стабильной работы восстановления в условиях меняющейся конфигурации, отказ от стандартного ПрочитатьXML в пользу ручного перебора узлов или использования DOM-модели является единственным надежным решением. Это позволит вашей системе сохранять живучесть даже после масштабных обновлений и рефакторинга метаданных.

← На главную