Работа с XML-файлами в среде 1С:Предприятие 7.7 остается актуальной задачей, особенно при организации обмена данными между старыми и современными системами. Часто возникает потребность не просто найти конкретное значение по известному адресу, а "прочитать" файл целиком, вне зависимости от его структуры. Рассмотрим, как это сделать правильно, эффективно и какие подводные камни могут встретиться на пути программиста.
Прежде чем приступать к написанию кода, разберем, какой инструмент выбрать. В операционной системе Windows за работу с XML отвечает библиотека MSXML (Microsoft XML Core Services). Для 1С 7.7 доступны два основных способа взаимодействия с ней:
В рамках этой статьи мы проанализируем наиболее востребованный и простой в реализации способ — рекурсивный обход через модель DOM.
Для начала работы нам необходимо создать COM-объект. В старых примерах часто используется Microsoft.XMLDOM, однако для повышения стабильности и поддержки современных стандартов рекомендуется использовать более конкретные версии библиотеки, например, MSXML2.DOMDocument.6.0.
Рассмотрим процедуру инициализации и запуска обхода:
Процедура прТестовоеЧтение()
// Проверяем, выбрано ли имя файла на форме
Если ПустаяСтрока(ВыбФайл) = 1 Тогда
Сообщить("Файл не выбран!", "i");
Возврат;
КонецЕсли;
// Создаем объект DOMDocument
оТест = СоздатьОбъект("MSXML2.DOMDocument.6.0");
оТест.async = 0; // Отключаем асинхронную загрузку
оТест.resolveExternals = 0;
// Загружаем файл
Если оТест.Load(ВыбФайл) = 0 Тогда
Сообщить("Ошибка при загрузке XML: " + оТест.parseError.reason);
Возврат;
КонецЕсли;
// Получаем корневой элемент
Корень = оТест.DocumentElement;
ОчиститьОкноСообщений();
// Запускаем рекурсивную печать узлов
прПечатьУзлов(оТест, Корень, 0);
КонецПроцедуры
Основная сложность универсального чтения заключается в том, что мы не знаем уровень вложенности тегов. Поэтому мы воспользуемся рекурсией — функция будет вызывать саму себя для каждого дочернего узла.
Проанализируем исправленный и оптимизированный алгоритм, который корректно обрабатывает теги, атрибуты и текстовое содержимое:
Процедура прПечатьУзлов(xmlDoc, Узел, Уровень = 0)
// Отображаем прогресс в строке состояния
Состояние("Обработка уровня: " + Уровень);
// Получаем количество дочерних элементов
ЧислоДетей = Узел.childNodes.length;
Для Инд = 1 По ЧислоДетей Цикл
// Извлекаем текущий узел (индексация в COM начинается с 0)
ТекУзел = Узел.childNodes.item(Инд - 1);
// Нас интересуют только узлы типа "Элемент" (nodeType = 1)
Если ТекУзел.nodeType <> 1 Тогда
Продолжить;
КонецЕсли;
// Формируем отступ для визуализации иерархии в окне сообщений
Отступ = Формат("", "С" + Уровень * 4);
// --- БЛОК ОБРАБОТКИ АТРИБУТОВ ---
СтрАтрибуты = "";
Если ТекУзел.attributes.length > 0 Тогда
Для Инд2 = 1 По ТекУзел.attributes.length Цикл
Атрибут = ТекУзел.attributes.item(Инд2 - 1);
СтрАтрибуты = СтрАтрибуты + ?(СтрАтрибуты = "", "", "; ") + Атрибут.nodeName + "=" + Атрибут.value;
КонецЦикла;
КонецЕсли;
// --- БЛОК ОБРАБОТКИ ТЕКСТА ---
Текст = "";
// Если у узла ровно один потомок и это текстовый узел (nodeType = 3)
Если ТекУзел.childNodes.length = 1 Тогда
Если ТекУзел.childNodes.item(0).nodeType = 3 Тогда
Текст = ТекУзел.childNodes.item(0).text;
КонецЕсли;
КонецЕсли;
// Выводим результат: ИмяТега (Атрибуты) : Текст
СтрРезультат = Отступ + ТекУзел.tagName + ?(СтрАтрибуты = "", "", " [" + СтрАтрибуты + "]") + " : " + Текст;
Сообщить(СтрРезультат);
// РЕКУРСИЯ: идем вглубь к детям текущего узла
прПечатьУзлов(xmlDoc, ТекУзел, Уровень + 1);
КонецЦикла;
КонецПроцедуры
Рассмотрим подробнее, почему код написан именно так, и какие ошибки мы предотвращаем:
nodeType: В XML-документе помимо тегов (элементов) существуют текстовые блоки, комментарии и секции CDATA. Если мы попытаемся прочитать tagName у комментария, программа выдаст ошибку. Мы фильтруем только элементы (тип 1).ТекУзел. Типичная ошибка начинающих — обращение к атрибутам родительского узла внутри цикла по детям.<Цена>100</Цена>, в модели DOM считается дочерним узлом типа "Text" (тип 3). Мы проверяем наличие такого узла, чтобы вывести значение 100.Для обычно начинаются с 1, а коллекции в MSXML начинаются с 0. Поэтому мы используем item(Инд - 1).При работе с реальными файлами (например, выгрузками из налоговой или ЭДО) вы можете столкнуться со следующими ситуациями:
1. Некорректные символы в файле.
Иногда XML содержит "грязные" данные. Например, внутри значения атрибута могут стоять двойные кавычки без экранирования: НаимОрг="ООО "Компания"". Такой файл не является валидным XML. Стандартный парсер выдаст ошибку при загрузке.
Решение: Перед загрузкой прочитайте файл как обычный текст через Текст = СоздатьОбъект("Текст"), выполните замену replace недопустимых комбинаций на ", и только потом сохраните и загружайте через DOM.
2. Медленная работа при больших объемах.
Функция Сообщить() в 1С 7.7 работает довольно медленно. Если вы используете обход для загрузки данных в справочники, уберите вывод в окно сообщений. Это ускорит процесс в несколько раз. Также помните про ограничение памяти: файлы более 100-200 Мб лучше обрабатывать частями или использовать XPath-запросы.
3. Пространства имен (Namespaces).
Если в корне тега указано xmlns="http://...", то поиск узлов через selectNodes может не работать. Однако при полном рекурсивном обходе через childNodes эта проблема обычно не возникает, так как мы идем по физическому дереву документа.
Если вам не нужно обходить весь файл, а требуется найти конкретный узел, изучите методы selectSingleNode и selectNodes. Они поддерживают язык запросов XPath, который позволяет писать выражения вида:
//СтрокаДокумента[@Номер='5']/Цена. Это работает значительно быстрее ручного перебора в циклах 1С.
Проанализировав приведенные примеры, вы сможете создать надежный и универсальный механизм импорта данных из XML-файлов любой сложности в вашей конфигурации 1С 7.7.