Рассмотрим частую задачу, с которой сталкиваются программисты при поддержке старых баз на платформе 1С 7.7 — замену устаревшей внешней компоненты v7plus.dll на более современные и стабильные инструменты для работы с XML-документами.
Разберем основные причины, по которым отказ от v7plus.dll является правильным шагом:
Посмотрим на классический способ загрузки и обхода XML-документа с помощью библиотеки Microsoft XML (MSXML). Прямым аналогом загрузки документа является объект MSXML2.DOMDocument.6.0.
Проанализируем пример чтения файла и рекурсивного обхода его узлов. Создадим процедуру, которая будет выводить дерево узлов и их атрибуты:
Процедура ОбработатьУзел(Узел, Уровень=0)
Состояние(Уровень);
КолвоУзлов = Узел.childNodes.length;
Для СчУзлов = 0 По КолвоУзлов-1 Цикл
ТекУзел = Узел.childNodes.item(СчУзлов);
Если ТекУзел.nodeType <> 1 Тогда // пропускаем текстовые узлы
Продолжить;
КонецЕсли;
Стр = Формат("","С"+Уровень*8);
СтрАтрибуты = "";
Для счАтрибутов = 0 По ТекУзел.attributes.length-1 Цикл
Атрибут = ТекУзел.attributes.item(счАтрибутов);
СтрАтрибуты = СтрАтрибуты + ?(СтрАтрибуты="","",";") + Атрибут.nodename+"="+Атрибут.value;
КонецЦикла;
ТекстУзла = "";
Если ТекУзел.childNodes.length = 1 Тогда
Если ТекУзел.childNodes.item(0).nodetype = 3 Тогда
ТекстУзла = ТекУзел.childNodes.item(0).text;
КонецЕсли;
КонецЕсли;
Стр = Стр + ТекУзел.tagName + ?(СтрАтрибуты="","","("+СтрАтрибуты+")" ) + ": " + ТекстУзла;
Сообщить(Стр);
// Рекурсивный вызов для дочерних узлов
ОбработатьУзел(ТекУзел, Уровень+1);
КонецЦикла;
Состояние("");
КонецПроцедуры
Вызовем эту процедуру, передав ей корневой элемент загруженного документа:
Иксемель = СоздатьОбъект("Microsoft.XMLDOM"); // или MSXML2.DOMDocument.6.0
Иксемель.Load(ФайлДанных);
Корень = Иксемель.DocumentElement;
ОбработатьУзел(Корень, 0);
Иксемель = "";
Платформа 1С 7.7 работает в кодировке Windows-1251. Если мы читаем XML-файл в кодировке UTF-8 стандартными методами, мы можем получить "кракозябры". Разберем, как решить эту проблему с помощью объекта ADODB.Stream.
Рассмотрим фрагмент кода, где мы получаем ответ по HTTP (или читаем из файла), а затем декодируем его в нужную кодировку перед загрузкой в парсер:
Стрим = СоздатьОбъект("ADODB.Stream");
Стрим.Mode = 3;
Стрим.Type = 1;
Стрим.Open();
// ... здесь мы пишем данные в поток (например, WinHttp.responseBody) ...
Стрим.Position = 0;
Стрим.Type = 2; // Текстовые данные
Стрим.Charset = "utf-8";
Ответ = Стрим.ReadText();
Стрим.Close();
XML_DOM = CreateObject("MSXML2.DOMDocument.6.0");
XML_DOM.LoadXML(Ответ); // Загружаем корректно раскодированный текст
Если вы не хотите сильно менять существующий код, проанализируем подход с написанием универсальных функций. Они будут проверять, доступна ли компонента v7plus.dll, и если нет (или если нужно форсировать использование MSXML), будут использовать методы DOMDocument.
Посмотрим на пример такой универсальной функции получения дочернего узла:
Функция ВыбратьУзел(РодительскийЭлемент, ИмяЭлементаДляПоиска, ПропускатьОшибки = 0) Экспорт
Если ПустоеЗначение(РодительскийЭлемент) = 1 Тогда
Возврат ПолучитьПустоеЗначение();
ИначеЕсли (ИспользоватьV7plus = 0) ИЛИ (КомпонентаV7plusЗагружена = 2) Тогда
// Используем методы MSXML
ИмяЭлементаДляПоиска = Сред(ИмяЭлементаДляПоиска, Найти(ИмяЭлементаДляПоиска,":")+1);
Для й = 0 По РодительскийЭлемент.ChildNodes.length-1 Цикл
Узел = РодительскийЭлемент.ChildNodes.item(й);
Если Узел.baseName = ИмяЭлементаДляПоиска Тогда
Возврат Узел;
КонецЕсли;
КонецЦикла;
Иначе
// Используем методы v7plus
Узел = РодительскийЭлемент.ВыбратьУзел(ИмяЭлементаДляПоиска);
Если ПустоеЗначение(Узел)=0 Тогда
Возврат Узел;
КонецЕсли;
КонецЕсли;
Если ПропускатьОшибки = 0 Тогда
Сообщить("В файле XML не удалось найти элемент: " + ИмяЭлементаДляПоиска, "!");
КонецЕсли;
Возврат ПолучитьПустоеЗначение();
КонецФункции
Если вам нужно обрабатывать файлы обмена гигантских размеров (сотни мегабайт), обычный DOM-парсер (и MSXML, и v7plus) может не справиться, так как полностью загружает файл в оперативную память. Выясним, какие существуют альтернативы:
MSXML2.SAXXMLReader. В отличие от DOM, он не загружает весь документ в память, а читает его последовательно, как поток (stream), вызывая события при нахождении каждого нового тега. Это полный аналог метода СоздатьПоследовательноСчитываемыйДокумент().1cpp.dll (1С++) или OpenXML от Romix. Они содержат оптимизированные методы работы с XML и текстом.Scripting.FileSystemObject или штатный класс 1С Текст. Читая файл построчно, можно искать нужные значения через строковые функции Найти() и Сред(). Этот метод требует больше ручного кода, но работает невероятно быстро и потребляет минимум оперативной памяти.