Как исправить ошибку преобразования данных XML при вызове ПрочитатьXML() в 1С?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При обмене данными, обновлении конфигураций или загрузке внешних файлов программисты 1С часто сталкиваются с неприятной ошибкой: "Ошибка преобразования данных XML". Особенно обидно, когда сбой происходит на стандартной системной строке ПрочитатьXML(ЧтениеXML), а сам файл XML кажется абсолютно валидным, хорошо структурированным и без проблем открывается в любом браузере или текстовом редакторе.

В этой статье мы подробно проанализируем, почему возникает эта ошибка на узлах с типизацией (например, при попытке прочитать ссылку вида <Пользователь xsi:type="CatalogRef.Пользователи">), разберем неочевидные системные причины и выработаем надежные программные и административные методы обхода этой проблемы.

В чем суть проблемы и почему «взрывается мозг» у платформы?

Когда мы вызываем метод ПрочитатьXML(), платформа 1С выполняет не просто чтение текстовых тегов. Она пытается провести автоматическую десериализацию — сопоставить текстовые данные и структуру XML с реальными объектами метаданных и типами в базе данных (используя механизмы XDTO). Если в XML-файле указан конкретный тип данных через атрибут xsi:type, платформа обязана строго преобразовать текстовый GUID в объектную ссылку 1С.

Сбой на конкретной строке с GUID (например, c9dc71b9-2dc0-11e8-b7f7-005056bba2e2) обычно сигнализирует об одной из нескольких глубоких проблем в базе данных или самом файле.

Рассмотрим по шагам все возможные причины этой ошибки и эффективные способы их устранения.

---

Решение 1. Поиск и устранение коллизии GUID в базе данных

Как показывает практика практикующих разработчиков, наиболее частой «скрытой» причиной сбоя ПрочитатьXML() является логическая коллизия данных в принимающей (или исходной) базе данных.

Представим ситуацию: в базе данных существует элемент справочника с определенным GUID. В то же время в процессе некорректного переноса данных или сбоя при обновлении конфигурации была создана группа (иерархический родитель) с точно таким же GUID. Либо система пытается записать GUID группы в реквизит, который жестко ожидает только элементы справочника.

Проанализируем алгоритм локализации этой проблемы:

  1. Определяем проблемный объект. По точке останова в конфигураторе на строке с ошибкой найдите точное имя файла во временной папке (например, v8_10FC_1ce.xml).
  2. Откройте этот файл через текстовый редактор (лучше использовать Notepad++ или аналогичный, способный работать с большими файлами) и перейдите на строку, указанную в ошибке (например, строка 110, позиция 6).
  3. Скопируйте UUID проблемного объекта: c9dc71b9-2dc0-11e8-b7f7-005056bba2e2.
  4. Выполните поиск этого UUID в вашей базе данных. Быстрее и надежнее всего сделать это с помощью несложного запроса или через консоль запросов, выполнив поиск по битым ссылкам, либо написав небольшую обработку:

ИскомыйGUID = Новый УникальныйИдентификатор("c9dc71b9-2dc0-11e8-b7f7-005056bba2e2");
СсылкаНаОбъект = Справочники.Пользователи.ПолучитьСсылка(ИскомыйGUID);

Если СсылкаНаОбъект.ПолучитьОбъект() = Неопределено Тогда
    Сообщить("Элемент в базе отсутствует, но возможно повреждена структура!");
Иначе
    ТекущийОбъект = СсылкаНаОбъект.ПолучитьОбъект();
    Сообщить("Найден объект: " + ТекущийОбъект.Наименование + ", ЭтоГруппа: " + ?(ТекущийОбъект.Метаданные().Иерархический И ТекущийОбъект.ЭтоГруппа, "Да", "Нет"));
КонецЕсли;

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

---

Решение 2. Программный обход с помощью ВозможностьЧтенияXML()

Если баз для обновления очень много, и заходить в каждую для ручного поиска «битых» элементов не представляется возможным, нам необходимо научить систему реагировать на такие узлы гибко. Для этого отлично подходит упреждающая проверка с использованием функции ВозможностьЧтенияXML().

Давайте посмотрим на пример кода, который предотвращает аварийное завершение работы всей обработки:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);

Пока ЧтениеXML.Прочитать() Цикл
    // Проверяем, что мы находимся на начале элемента, который хотим десериализовать
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
        
        // Проверяем, сможет ли платформа прочитать этот узел без вылета в ошибку
        Если ВозможностьЧтенияXML(ЧтениеXML) Тогда
            Попытка
                ОбъектДесериализации = ПрочитатьXML(ЧтениеXML);
                // Выполняем дальнейшую обработку объекта
            Исключение
                ЗаписьЖурналаРегистрации("ЧтениеXML", УровеньЖурналаРегистрации.Предупреждение, , , 
                    "Ошибка при чтении узла: " + ЧтениеXML.Имя + ". Описание: " + ОписаниеОшибки());
                ЧтениеXML.Пропустить(); // Пропускаем проблемный узел
            КонецПопытки;
        Иначе
            // Если платформа заранее "понимает", что тип прочитать не удастся
            ЗаписьЖурналаРегистрации("ЧтениеXML", УровеньЖурналаРегистрации.Предупреждение, , , 
                "Невозможно прочитать тип узла: " + ЧтениеXML.Имя);
            ЧтениеXML.Пропустить(); // Корректно переходим к следующему узлу XML
        КонецЕсли;
        
    КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();

Использование метода ЧтениеXML.Пропустить() позволяет пропустить некорректный узел и продолжить разбор остальной части файла, не прерывая выполнение длительного процесса загрузки или обновления.

---

Решение 3. Ручной построчный парсинг файла (Обход ограничений XDTO)

Иногда автоматический механизм десериализации 1С блокирует чтение из-за того, что в принимающей базе слегка изменены метаданные (например, справочник переименован или временно отсутствует). В таких сценариях мы можем полностью отказаться от автоматического ПрочитатьXML() на проблемных тегах и распарсить их вручную как обычные текстовые узлы.

Проанализируем пример ручного разбора, где мы извлекаем строковый GUID напрямую из XML-тега без привлечения механизмов типизации платформы:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);

Пока ЧтениеXML.Прочитать() Цикл
    // Ищем интересующий нас тег, даже если его тип вызывает ошибку автоматического распознавания
    Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента И ЧтениеXML.Имя = "Пользователь" Тогда
        
        // Получаем значение атрибута типа (если нужно проанализировать его программно)
        ТипИзXML = ЧтениеXML.ПолучитьАтрибут("xsi:type");
        
        // Читаем GUID как обычную строку текста, минуя строгую проверку типов 1С
        СтрокаGUID = ЧтениеXML.ПрочитатьСтроку();
        
        Попытка
            // Создаем уникальный идентификатор вручную
            НовыйИдентификатор = Новый УникальныйИдентификатор(СтрокаGUID);
            СсылкаНаПользователя = Справочники.Пользователи.ПолучитьСсылка(НовыйИдентификатор);
            
            // Проверяем валидность ссылки в нашей базе
            Если СсылкаНаПользователя.ПолучитьОбъект() = Неопределено Тогда
                Сообщить("Предупреждение: Ссылка с GUID " + СтрокаGUID + " создана, но объекта в БД нет.");
            КонецЕсли;
        Исключение
            Сообщить("Некорректный формат GUID в файле: " + СтрокаGUID);
        КонецПопытки;
        
    КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();

Этот способ дает разработчику 100% контроль над процессом чтения и гарантирует, что программа не завершится аварийно из-за несоответствия схем XDTO или отсутствия пространства имен xmlns:xsi.

---

Решение 4. Текстовая предобработка XML-файла перед загрузкой

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

Рассмотрим пример функции, которая вырезает атрибуты xsi:type из файла, превращая строго типизированные узлы в обычные текстовые элементы, которые 1С прочитает без лишних вопросов:


Функция ОчиститьФайлXMLОтТипизации(ИмяФайлаИсточника, ИмяФайлаПриемника) Экспорт
    
    ТекстДокумент = Новый ТекстовыйДокумент;
    ТекстДокумент.Прочитать(ИмяФайлаИсточника, КодировкаТекста.UTF8);
    ПолныйТекст = ТекстДокумент.ПолучитьТекст();
    
    // Удаляем информацию о строгой типизации XDTO для конкретного справочника
    // Было: <Пользователь xsi:type="CatalogRef.Пользователи">
    // Станет: <Пользователь>
    ПроблемнаяСтрока = " xsi:type=""CatalogRef.Пользователи""";
    ОчищенныйТекст = СтрЗаменить(ПолныйТекст, ПроблемнаяСтрока, "");
    
    // Записываем очищенный файл для последующего стандартного чтения
    ТекстДокумент.УстановитьТекст(ОчищенныйТекст);
    ТекстДокумент.Записать(ИмяФайлаПриемника, КодировкаТекста.UTF8);
    
    Возврат ИмяФайлаПриемника;
КонецФункции

После такой предварительной обработки метод ПрочитатьXML() больше не будет пытаться сопоставить типы данных на уровне парсера XML и позволит вам обработать информацию в кодовой логике самостоятельно.

---

Решение 5. Сброс и очистка кэша метаданных

В редких случаях структура XML-файла и данные в базе данных абсолютно верны, но ошибка преобразования все равно упорно появляется на стороне конкретного пользователя или сервера. Это может происходить из-за рассинхронизации локального кэша схем XDTO платформы 1С.

Для исправления этой проблемы выполните следующие действия:

  1. Завершите все активные сеансы пользователей в базе данных.
  2. Запустите сеанс 1С с параметром командной строки /ClearCache (это принудительно очистит кэш метаданных текущего сеанса).
  3. В случае клиент-серверного варианта работы выполните перезапуск службы сервера «1С:Предприятие» для полной очистки серверного кэша.

Применение этих комплексных мер гарантирует надежное чтение любых файлов обмена и защитит ваши обработки от непредвиденных аварийных остановок.

← На главную