При обмене данными, обновлении конфигураций или загрузке внешних файлов программисты 1С часто сталкиваются с неприятной ошибкой: "Ошибка преобразования данных XML". Особенно обидно, когда сбой происходит на стандартной системной строке ПрочитатьXML(ЧтениеXML), а сам файл XML кажется абсолютно валидным, хорошо структурированным и без проблем открывается в любом браузере или текстовом редакторе.
В этой статье мы подробно проанализируем, почему возникает эта ошибка на узлах с типизацией (например, при попытке прочитать ссылку вида <Пользователь xsi:type="CatalogRef.Пользователи">), разберем неочевидные системные причины и выработаем надежные программные и административные методы обхода этой проблемы.
Когда мы вызываем метод ПрочитатьXML(), платформа 1С выполняет не просто чтение текстовых тегов. Она пытается провести автоматическую десериализацию — сопоставить текстовые данные и структуру XML с реальными объектами метаданных и типами в базе данных (используя механизмы XDTO). Если в XML-файле указан конкретный тип данных через атрибут xsi:type, платформа обязана строго преобразовать текстовый GUID в объектную ссылку 1С.
Сбой на конкретной строке с GUID (например, c9dc71b9-2dc0-11e8-b7f7-005056bba2e2) обычно сигнализирует об одной из нескольких глубоких проблем в базе данных или самом файле.
Рассмотрим по шагам все возможные причины этой ошибки и эффективные способы их устранения.
---
Как показывает практика практикующих разработчиков, наиболее частой «скрытой» причиной сбоя ПрочитатьXML() является логическая коллизия данных в принимающей (или исходной) базе данных.
Представим ситуацию: в базе данных существует элемент справочника с определенным GUID. В то же время в процессе некорректного переноса данных или сбоя при обновлении конфигурации была создана группа (иерархический родитель) с точно таким же GUID. Либо система пытается записать GUID группы в реквизит, который жестко ожидает только элементы справочника.
Проанализируем алгоритм локализации этой проблемы:
v8_10FC_1ce.xml).c9dc71b9-2dc0-11e8-b7f7-005056bba2e2.
ИскомыйGUID = Новый УникальныйИдентификатор("c9dc71b9-2dc0-11e8-b7f7-005056bba2e2");
СсылкаНаОбъект = Справочники.Пользователи.ПолучитьСсылка(ИскомыйGUID);
Если СсылкаНаОбъект.ПолучитьОбъект() = Неопределено Тогда
Сообщить("Элемент в базе отсутствует, но возможно повреждена структура!");
Иначе
ТекущийОбъект = СсылкаНаОбъект.ПолучитьОбъект();
Сообщить("Найден объект: " + ТекущийОбъект.Наименование + ", ЭтоГруппа: " + ?(ТекущийОбъект.Метаданные().Иерархический И ТекущийОбъект.ЭтоГруппа, "Да", "Нет"));
КонецЕсли;
Если выясняется, что в базе под этим GUID живет группа, а XML-файл пытается загрузить его как элемент (или наоборот), удалите битый элемент из базы данных либо перегенерируйте его UUID. После очистки коллизии повторите чтение XML — в большинстве случаев загрузка сразу проходит успешно.
---
Если баз для обновления очень много, и заходить в каждую для ручного поиска «битых» элементов не представляется возможным, нам необходимо научить систему реагировать на такие узлы гибко. Для этого отлично подходит упреждающая проверка с использованием функции ВозможностьЧтенияXML().
Давайте посмотрим на пример кода, который предотвращает аварийное завершение работы всей обработки:
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьФайл(ПутьКФайлу);
Пока ЧтениеXML.Прочитать() Цикл
// Проверяем, что мы находимся на начале элемента, который хотим десериализовать
Если ЧтениеXML.ТипУзла = ТипУзлаXML.НачалоЭлемента Тогда
// Проверяем, сможет ли платформа прочитать этот узел без вылета в ошибку
Если ВозможностьЧтенияXML(ЧтениеXML) Тогда
Попытка
ОбъектДесериализации = ПрочитатьXML(ЧтениеXML);
// Выполняем дальнейшую обработку объекта
Исключение
ЗаписьЖурналаРегистрации("ЧтениеXML", УровеньЖурналаРегистрации.Предупреждение, , ,
"Ошибка при чтении узла: " + ЧтениеXML.Имя + ". Описание: " + ОписаниеОшибки());
ЧтениеXML.Пропустить(); // Пропускаем проблемный узел
КонецПопытки;
Иначе
// Если платформа заранее "понимает", что тип прочитать не удастся
ЗаписьЖурналаРегистрации("ЧтениеXML", УровеньЖурналаРегистрации.Предупреждение, , ,
"Невозможно прочитать тип узла: " + ЧтениеXML.Имя);
ЧтениеXML.Пропустить(); // Корректно переходим к следующему узлу XML
КонецЕсли;
КонецЕсли;
КонецЦикла;
ЧтениеXML.Закрыть();
Использование метода ЧтениеXML.Пропустить() позволяет пропустить некорректный узел и продолжить разбор остальной части файла, не прерывая выполнение длительного процесса загрузки или обновления.
---
Иногда автоматический механизм десериализации 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.
---
Если вы столкнулись с ситуацией, когда сторонний источник выгружает XML-файлы с некорректными типами (например, вместо валидных типов 1С подставляются сырые метаданные, не зарегистрированные в вашей конфигурации), можно применить метод текстовой очистки файла «на лету» перед тем, как передать его в объект ЧтениеXML.
Рассмотрим пример функции, которая вырезает атрибуты xsi:type из файла, превращая строго типизированные узлы в обычные текстовые элементы, которые 1С прочитает без лишних вопросов:
Функция ОчиститьФайлXMLОтТипизации(ИмяФайлаИсточника, ИмяФайлаПриемника) Экспорт
ТекстДокумент = Новый ТекстовыйДокумент;
ТекстДокумент.Прочитать(ИмяФайлаИсточника, КодировкаТекста.UTF8);
ПолныйТекст = ТекстДокумент.ПолучитьТекст();
// Удаляем информацию о строгой типизации XDTO для конкретного справочника
// Было: <Пользователь xsi:type="CatalogRef.Пользователи">
// Станет: <Пользователь>
ПроблемнаяСтрока = " xsi:type=""CatalogRef.Пользователи""";
ОчищенныйТекст = СтрЗаменить(ПолныйТекст, ПроблемнаяСтрока, "");
// Записываем очищенный файл для последующего стандартного чтения
ТекстДокумент.УстановитьТекст(ОчищенныйТекст);
ТекстДокумент.Записать(ИмяФайлаПриемника, КодировкаТекста.UTF8);
Возврат ИмяФайлаПриемника;
КонецФункции
После такой предварительной обработки метод ПрочитатьXML() больше не будет пытаться сопоставить типы данных на уровне парсера XML и позволит вам обработать информацию в кодовой логике самостоятельно.
---
В редких случаях структура XML-файла и данные в базе данных абсолютно верны, но ошибка преобразования все равно упорно появляется на стороне конкретного пользователя или сервера. Это может происходить из-за рассинхронизации локального кэша схем XDTO платформы 1С.
Для исправления этой проблемы выполните следующие действия:
/ClearCache (это принудительно очистит кэш метаданных текущего сеанса).Применение этих комплексных мер гарантирует надежное чтение любых файлов обмена и защитит ваши обработки от непредвиденных аварийных остановок.