При работе с внешними XML-файлами в 1С:Предприятии 7.7 разработчики часто сталкиваются с ситуацией, когда штатные средства компоненты v7plus.dll не позволяют быстро добраться до нужного подчиненного узла по его имени. Разберем причину возникновения таких ошибок и рассмотрим наиболее эффективные и надежные способы выборки данных из XML-документов.
Рассмотрим стандартный пример, с которым столкнулся автор темы. В XML-файле присутствует корневой элемент Rasshifrovka, содержащий подчиненные блоки ApplicationArea и DataArea. При попытке найти узел прямо из объекта документа вызовами Документ.ВыбратьУзел("ApplicationArea") или Документ.ВыбратьУзлы("ApplicationArea") система возвращает ошибку:
«Значение не представляет агрегатный объект (Наименование)»
Выясним причину такого поведения XMLParser из библиотеки v7plus:
ВыбратьУзел() без указания относительного или абсолютного пути ищет узел непосредственно на текущем уровне обращения. Если поиск выполняется от самого объекта Документ, метод ожидает, что ApplicationArea является корневым тегом. Поскольку корнем является Rasshifrovka, метод возвращает пустое значение (Неопределено или пустой объект). При попытке обратиться к свойству .Наименование у пустого результата возникает ошибка.v7plus необходимо явно задавать полные пути XPath:
Код правильного обращения через полный путь:
// Выборка с указанием полного пути
ApplicationArea = Документ.ВыбратьУзел("Rasshifrovka/ApplicationArea");
// Выборка с рекурсивным поиском по всему дереву
ApplicationArea = Документ.ВыбратьУзел("//ApplicationArea");
Однако штатная компонента v7plus имеет существенные ограничения по поддержке стандартов XML и зависима от версии библиотеки MSXML, установленной в операционной системе. Для полноценной и стабильной работы рекомендуем переходить на прямую работу с системным COM-объектом MSXML2.DOMDocument.
Использование COM-объекта MSXML2.DOMDocument предоставляет полный спектр возможностей стандартов DOM и XPath. Рассмотрим основные шаги по инициализации и загрузке документа в 1С 7.7.
Для предотвращения ошибок и зависаний при загрузке файлов важно задать правильные параметры объекта:
// Создаем объект MSXML2.DOMDocument
dom = СоздатьОбъект("MSXML2.DOMDocument.6.0");
// Отключаем асинхронную загрузку, чтобы код ждал полного открытия файла
dom.async = 0;
// Отключаем загрузку внешних DTD-схем и проверку валидности для ускорения
dom.resolveExternals = 0;
dom.validateOnParse = 0;
// Задаем язык запросов XPath для корректной работы selectSingleNode и selectNodes
dom.setProperty("SelectionLanguage", "XPath");
// Загружаем XML-файл
Если dom.load(ПолноеИмяФайла) = 0 Тогда
Предупреждение("Ошибка чтения XML файла!");
КонецЕсли;
Рассмотрим, как выполнять выборку данных с помощью языковых конструкций XPath. Метод selectSingleNode возвращает первое совпадение, а selectNodes — коллекцию найденных элементов.
Посмотрим на практический пример обхода табличной части (блока roster) и считывания шапки документа:
Функция ДанныеАтрибута(Узел, ИмяУзла)
Попытка
Возврат СокрЛП(Узел.selectSingleNode(ИмяУзла).text);
Исключение
Возврат "";
КонецФункции
КонецФункции
Процедура РазобратьXML()
// Чтение элемента из шапки по относительному пути
ОписаниеДоставки = ДанныеАтрибута(dom.documentElement, "deliveryCondition/contract/number");
Сообщить("Номер договора: " + ОписаниеДоставки);
// Получаем список всех строк товаров в документе
СтрокиТоваров = dom.documentElement.selectSingleNode("roster");
Если ПустоеЗначение(СтрокиТоваров) = 0 Тогда
КоличествоСтрок = СтрокиТоваров.childNodes.length;
Для Сч = 0 По КоличествоСтрок - 1 Цикл
ТекУзел = СтрокиТоваров.childNodes.item(Сч);
// Получаем значение подчиненного тега <number>
НомерПозиции = ДанныеАтрибута(ТекУзел, "number");
Сообщить("Позиция №: " + НомерПозиции);
КонецЦикла;
КонецЕсли;
КонецПроцедуры
В реальных условиях файлы от контрагентов могут не содержать отдельных необязательных тегов. Если применить selectSingleNode к несуществующему тегу и сразу обратиться к свойству .text, возникнет ошибка выполнения. Разберем универсальные вспомогательные функции для безопасного извлечения значений и узлов с помощью метода getElementsByTagName.
// Функция для безопасного получения значения тега по имени
Функция ПолучитьЗначениеУзлаПоИмени(Узел, Имя)
Результат = "";
Если ПустоеЗначение(Узел) = 1 Тогда
Возврат Результат;
КонецЕсли;
// Ищем все входящие элементы с указанным именем
Коллекция = Узел.getElementsByTagName(Имя);
Если Коллекция.length > 0 Тогда
Результат = Коллекция.Item(0).Text;
КонецЕсли;
Возврат Результат;
КонецФункции
// Функция для безопасного получения самого узла
Функция ПолучитьУзелПоИмени(Узел, Имя)
Результат = "";
Если ПустоеЗначение(Узел) = 1 Тогда
Возврат Результат;
КонецЕсли;
Коллекция = Узел.getElementsByTagName(Имя);
Если Коллекция.length > 0 Тогда
Результат = Коллекция.Item(0);
КонецЕсли;
Возврат Результат;
КонецФункции
Метод getElementsByTagName ищет теги с указанным именем на любой глубине вложенности относительно переданного узла, что позволяет легко находить глубоко расположенные элементы без построения полной цепочки родительских тегов.
Если обрабатываемый XML-файл содержит пространствo имен (например, <Rasshifrovka xmlns="http://site.com/schema">), стандартные методы поиска по имени тега могут вернуть пустой результат. Чтобы решить эту проблему в MSXML, необходимо зарегистрировать псевдоним пространства имен перед поиском:
// Регистрация префикса пространства имен
dom.setProperty("SelectionNamespaces", "xmlns:ns='http://site.com/schema'");
// Поиск узла с использованием заданного префикса ns
Узел = dom.selectSingleNode("//ns:ApplicationArea");
Если ПустоеЗначение(Узел) = 0 Тогда
Сообщить(Узел.text);
КонецЕсли;
Модель DOM загружает весь документ в оперативную память. Если размер XML-файла превышает 50–100 МБ, это может вызвать переполнение памяти в 1С 7.7. В таких ситуациях рекомендуется использовать последовательный SAX-парсер (MSXML2.SAXXMLReader.6.0), который считывает документ «на лету» событие за событием без загрузки всего дерева в память.
v7plus используйте рекурсивный синтаксис //ИмяТега или укажите полный путь от корня.MSXML2.DOMDocument.6.0.getElementsByTagName() или обрабатывайте исключения при обращении к selectSingleNode().text.