При работе с распределенными информационными базами (РИБ) специалисты часто сталкиваются со следующей проблемой: процесс обмена данными прерывается критической ошибкой «Ошибка преобразования данных XML». При этом система указывает путь к временному файлу обмена и конкретный номер строки, где произошел сбой. Попытки проанализировать указанную строку файла часто показывают, что структура XML-тегов абсолютно корректна, а повторный обмен на других объектах может выдавать ошибку уже в совершенно ином месте.
В этой статье мы подробно разберем природу возникновения данной ошибки, рассмотрим по шагам эффективную методику ее локализации и устранения, а также проанализируем альтернативные системные причины, способные вызывать подобные сбои в платформе 1С:Предприятие 8.
Для того чтобы эффективно бороться с проблемой, нам необходимо понять физику процесса. Чтение изменений в РИБ происходит с помощью вызова метода встроенного языка ПланыОбмена.ПрочитатьИзменения(). Этот метод считывает XML-файл сообщения обмена последовательно, десериализуя объекты XML обратно в объекты базы данных 1С.
Когда платформа выбрасывает исключение Ошибка преобразования данных XML, это сигнализирует об одной из следующих проблем:
0x00, символ конца файла 0x1A, мягкие переносы строк и другие невидимые ASCII-символы). Стандартный сериализатор 1С записывает их в XML, но строгий XML-парсер при чтении файла спотыкается на них и аварийно завершает работу.Рассмотрим пошагово каждое решение для устранения данной проблемы.
Если ошибка воспроизводится на конкретных элементах справочников или документах (как в случае автора темы с контрагентами), самым надежным способом будет их временный вынос из базы данных, проведение «пустой» синхронизации для закрытия транзакции обмена и последующий возврат очищенных объектов обратно.
Для реализации этого метода нам понадобятся стандартная обработка ВыгрузкаЗагрузкаДанныхXML.epf (ее можно найти на диске ИТС или в составе конфигурации «Конвертация данных») и простая самописная обработка для программного удаления объектов в режиме обхода проверок.
Выполним следующие шаги:
Шаг 1. Точная идентификация проблемного объекта
Когда при чтении изменений возникает ошибка, перейдем по указанному в ошибке пути к временному файлу (например, C:\Users\ИмяПользователя\AppData\Local\Temp\v8_XXXX_xx.xml). Откроем этот файл в любом продвинутом текстовом редакторе (Notepad++, VS Code) и найдем строку, указанную в ошибке. Нам нужно определить уникальный идентификатор (GUID) объекта и его тип. В XML-сообщении обмена это выглядит следующим образом:
<CatalogObject.Контрагенты>
<Ref>f1a02159-fc90-11db-9488-00016cb2604d</Ref>
<IsFolder>false</IsFolder>
<DeletionMark>false</DeletionMark>
...
</CatalogObject.Контрагенты>
В данном примере мы четко видим, что сбой вызывает элемент справочника Контрагенты с уникальным идентификатором ссылки f1a02159-fc90-11db-9488-00016cb2604d.
Шаг 2. Выгрузка объекта во внешний файл
Запустим обработку ВыгрузкаЗагрузкаДанныхXML.epf в базе-источнике (базе, откуда выгружаются данные). Настроим отбор по нашему сбойному объекту (по коду, наименованию или GUID) и произведем его выгрузку в отдельный внешний XML-файл. Это гарантирует, что мы не потеряем данные элемента.
Шаг 3. Физическое удаление объекта из информационной базы
Обычное интерактивное удаление объекта может быть заблокировано ссылочной целостностью или вызывать дополнительные ошибки. Нам необходимо удалить объект непосредственно из таблиц базы данных, обязательно установив свойство ОбменДанными.Загрузка = Истина. Это предотвратит повторную регистрацию изменений и отключит прикладные проверки при удалении. Напишем и выполним в консоли кода следующий фрагмент:
// Укажите тип справочника и GUID проблемного элемента
ИскомыйGUID = Новый УникальныйИдентификатор("f1a02159-fc90-11db-9488-00016cb2604d");
СсылкаНаОбъект = Справочники.Контрагенты.ПолучитьСсылка(ИскомыйGUID);
Если Не СсылкаНаОбъект.ПолучитьОбъект() = Неопределено Тогда
ОбъектБазы = СсылкаНаОбъект.ПолучитьОбъект();
ОбъектБазы.ОбменДанными.Загрузка = Истина;
Попытка
ОбъектБазы.Удалить();
Сообщить("Сбойный объект успешно удален из базы данных.");
Исключение
Сообщить("Не удалось удалить объект: " + ОписаниеОшибки());
КонецПопытки;
КонецЕсли;
Шаг 4. Проведение сеанса обмена данными
После удаления сбойного объекта запустим процедуру обмена в обе стороны. Так как сбойная запись больше не участвует в формировании пакета обмена, транзакция загрузки на периферийном узле успешно завершится. Номера принятых и отправленных сообщений в планах обмена синхронизируются.
Шаг 5. Восстановление объекта
Снова откроем обработку ВыгрузкаЗагрузкаДанныхXML.epf в базе-источнике и загрузим ранее сохраненный файл с объектом. При записи объекта через эту обработку платформа автоматически заново сформирует все индексы и таблицы в СУБД, исправив скрытые физические повреждения структуры данных, которые могли мешать работе XML-сериализатора.
После повторного обмена восстановленный объект успешно и без ошибок перенесется во все узлы РИБ.
Если объектов много или ручное удаление не представляется возможным, следует проверить базу данных на наличие недопустимых спецсимволов. Стандартная функция платформы НайтиНедопустимыеСимволыXML() не всегда улавливает некоторые специфические бинарные последовательности.
Проанализируем ситуацию и напишем универсальный программный код для очистки строковых реквизитов выбранного объекта (например, справочника контрагентов) от мусорных символов перед выгрузкой:
Функция ОчиститьСтрокуОтМусораXML(ВходнаяСтрока) Экспорт
// Определим регулярное выражение для поиска символов, недопустимых в XML 1.0
// Допускаются только: #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
Результат = "";
Для Позиция = 1 По СтрДлина(ВходнаяСтрока) Цикл
КодСимвола = КодСимвола(ВходнаяСтрока, Позиция);
// Фильтруем управляющие символы ASCII ниже 32 (кроме табуляции, перевода строки и возврата каретки)
Если (КодСимвола < 32) И (КодСимвола <> 9) И (КодСимвола <> 10) И (КодСимвола <> 13) Тогда
Продолжить; // Пропускаем недопустимый управляющий символ
КонецЕсли;
Результат = Результат + Сред(ВходнаяСтрока, Позиция, 1);
КонецЦикла;
Возврат Результат;
КонецФункции
Процедура ОчиститьРеквизитыКонтрагента(СсылкаНаКонтрагента) Экспорт
КонтрагентОбъект = СсылкаНаКонтрагента.ПолучитьОбъект();
Если КонтрагентОбъект <> Неопределено Тогда
КонтрагентОбъект.ОбменДанными.Загрузка = Истина;
// Очищаем основные строковые реквизиты
КонтрагентОбъект.Наименование = ОчиститьСтрокуОтМусораXML(КонтрагентОбъект.Наименование);
КонтрагентОбъект.НаименованиеПолное = ОчиститьСтрокуОтМусораXML(КонтрагентОбъект.НаименованиеПолное);
КонтрагентОбъект.ДополнительноеОписание = ОчиститьСтрокуОтМусораXML(КонтрагентОбъект.ДополнительноеОписание);
КонтрагентОбъект.Комментарий = ОчиститьСтрокуОтМусораXML(КонтрагентОбъект.Комментарий);
Если КонтрагентОбъект.Модифицирован() Тогда
КонтрагентОбъект.Записать();
Сообщить("Объект " + СсылкаНаКонтрагента + " очищен от некорректных символов XML.");
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот метод эффективно убирает любые невидимые символы, скопированные сотрудниками из текстовых редакторов или веб-страниц, сохраняя при этом текстовое содержимое реквизитов в целости.
Часто сбой чтения XML возникает из-за некорректного обновления конфигурации. Если вы обновили главную базу динамически (так называемое «демоническое» обновление), конфигурация базы данных на сервере и у клиентов может различаться. При выгрузке изменений периферийная база может отправлять XML-структуру, ссылающуюся на метаданные старого формата.
Рассмотрим, как правильно обновить кэш и конфигурацию периферийного узла:
%localappdata%\1C\1Cv8 и %appdata%\1C\1Cv8 и полностью удалите все папки с длинными буквенно-цифровыми именами (это кэш метаданных 1С).
ПланыОбмена.УстановитьГлавныйУзел(Неопределено);
.cf файл.
ПланыОбмена.УстановитьГлавныйУзел(ПланыОбмена.Полный.НайтиПоКоду("МСК")); // Укажите код вашего главного узла
После этой процедуры структуры метаданных гарантированно станут абсолютно идентичными на уровне SQL-идентификаторов таблиц, и любые ошибки сопоставления тегов XML прекратятся.
Если выгрузка успешно завершается при уменьшении объема данных (как заметил автор темы: «Если снимать регистрацию, и делать поменьше объем данных — обмен успешно завершается»), мы имеем дело с классическим переполнением оперативной памяти парсером XML.
Для решения этой проблемы необходимо уменьшить размер пакета данных, обрабатываемого в рамках одной транзакции СУБД. Проанализируем настройки планов обмена и уменьшим количество элементов в транзакции:
0 (загружать все одним пакетом) или 1000.100 или 200. Это заставит платформу периодически закрывать транзакцию записи в базу данных и очищать оперативную память от накопленного DOM-дерева XML-файла.Использование этих четырех системных подходов позволит вам гарантированно победить ошибку преобразования XML при обмене РИБ и обеспечить бесперебойную и стабильную работу распределенной ИТ-инфраструктуры вашего предприятия.