При обработке XML-документов в 1С (например, файлов графических схем или данных интеграции) часто возникает ситуация, когда разные теги документа находятся в разных пространствах имён (xmlns). Если родительский элемент относится к одному URI, а вложенный переопределяет пространство имён, стандартный выбор узлов через XPath может возвращать пустой результат.
Разберем, почему так происходит, и проанализируем основные способы решения этой задачи в платформе 1С.
В стандарте XPath 1.0 элементы, находящиеся в пространстве имён по умолчанию (объявленные через xmlns="..."), требуют явного указания префикса. Если автоматический РазыменовательПИ создан для узла с пространством имён схемы (например, http://v8.1c.ru/8.2/data/graphscheme), он не сможет автоматически сопоставить вложенные узлы <item>, принадлежащие другому пространству имён (например, http://v8.1c.ru/8.1/data/core).
Рассмотрим вариант, когда выполнение поиска разбивается на два последовательных шага. Сначала мы находим контейнер в основном пространстве имён схемы, а затем создаем новый разыменователь для вложенного пространства имён и ищем требуемые элементы.
Посмотрим на пример реализации:
// 1. Поиск элемента с основным пространством имён
РазыменовательЭталон = ДокументDOMЭталон.СоздатьРазыменовательПИ();
Результат = ДокументDOMЭталон.ВычислитьВыражениеXPath(
"./xmlns:description",
ЭлементDOMВариант,
РазыменовательЭталон,
ТипРезультатаDOMXPath.НеупорядоченныйИтераторУзлов
);
ЭлементDOMНаименованиеВарианта = Результат.ПолучитьСледующий();
// 2. Создание разыменователя для v8 core и выборка вложенных тегов
Если ЭлементDOMНаименованиеВарианта <> Неопределено Тогда
Разыменовательv8core = ДокументDOMЭталон.СоздатьРазыменовательПИ("xmlns", "http://v8.1c.ru/8.1/data/core");
Результат = ДокументDOMЭталон.ВычислитьВыражениеXPath(
"./xmlns:item/xmlns:content",
ЭлементDOMНаименованиеВарианта,
Разыменовательv8core,
ТипРезультатаDOMXPath.НеупорядоченныйИтераторУзлов
);
СвойствоЭлементаDOM = Результат.ПолучитьСледующий();
Пока СвойствоЭлементаDOM <> Неопределено Цикл
// Обработка найденных узлов
СвойствоЭлементаDOM = Результат.ПолучитьСледующий();
КонецЦикла;
КонецЕсли;
Чтобы найти узлы за один подход без пересоздания разыменователей, можно обращаться к тегам через функцию XPath local-name(). Это позволяет искать узлы по их имени независимо от того, какое пространство имён или префикс им назначены.
Проанализируем пример единого запроса:
ВыражениеXPath = "./*[local-name()='description']/*[local-name()='item']/*[local-name()='content']";
Результат = ДокументDOMЭталон.ВычислитьВыражениеXPath(
ВыражениеXPath,
ЭлементDOMВариант,
Неопределено,
ТипРезультатаDOMXPath.НеупорядоченныйИтераторУзлов
);
НайденныйУзел = Результат.ПолучитьСледующий();
Пока НайденныйУзел <> Неопределено Цикл
// Обработка текста или атрибутов узла
НайденныйУзел = Результат.ПолучитьСледующий();
КонецЦикла;
Если требуется строго ограничить поиск конкретным пространством имён для отдельного тега, выражение можно усложнить с помощью проверки namespace-uri():
ВыражениеXPath = "./*[local-name()='description']/*[local-name()='item' and namespace-uri()='http://v8.1c.ru/8.1/data/core']/*[local-name()='content']";
Ещё один вариант решения в 1С — сопоставить каждое используемое пространство имён собственному префиксу. Для этого передадим объект Соответствие в конструктор РазыменовательПространствИменDOM.
Разберем по шагам создание такого разыменователя:
Соответствие и заполняем пары "Префикс" — "URI пространства имён".РазыменовательПространствИменDOM с переданным соответствием.
СоответствиеПИ = Новый Соответствие;
СоответствиеПИ.Вставить("gs", "http://v8.1c.ru/8.2/data/graphscheme");
СоответствиеПИ.Вставить("core", "http://v8.1c.ru/8.1/data/core");
Разыменователь = Новый РазыменовательПространствИменDOM(СоответствиеПИ);
ВыражениеXPath = "./gs:description/core:item/core:content";
Результат = ДокументDOMЭталон.ВычислитьВыражениеXPath(
ВыражениеXPath,
ЭлементDOMВариант,
Разыменователь,
ТипРезультатаDOMXPath.НеупорядоченныйИтераторУзлов
);
Вывод: Если структура XML содержит динамические или сложные пространства имён, удобнее всего использовать функцию local-name(). Если же пространства имён строго определены стандартом обмена, наиболее чистым решением будет явное задание префиксов через Соответствие и РазыменовательПространствИменDOM.