Я хочу попробовать развернуть файл Excel XML Spreadsheet с помощью MSXML и XPath.Может ли MSXML XPath выбирать атрибуты? (UPD: реальная проблема была с пространством имен без префикса по умолчанию)
- https://technet.microsoft.com/en-us/magazine/2006.01.blogtales
- https://msdn.microsoft.com/en-us/library/aa140066.aspx
Он имеет корневой элемент <Workbook xmlns.... xmlns....>
и кучу узлов следующего уровня <Worksheet ss:Name="xxxx">
.
<?xml version="1.0" encoding="UTF-8"?>
<?mso-application progid="Excel.Sheet"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:html="http://www.w3.org/TR/REC-html40">
....
<Worksheet ss:Name="Карточка">
....
</Worksheet>
<Worksheet ss:Name="Баланс">
...
...
...
</Worksheet>
</Workbook>
На определенном этапе я хочу использовать XPath, чтобы получить самые имена рабочих листов.
ПРИМЕЧАНИЕ. Я не хочу, чтобы получить имена косвенно, то есть сначала выбрать те узлы Worksheet
, а затем перечислить их вручную, прочитать их дочерние атрибуты ss:Name
. Это я могу сделать, и это не тема здесь.
Я хочу использовать гибкость XPath: прямое получение этих узлов ss:Name
без дополнительных слоев косвенности.
procedure DoParseSheets(FileName: string);
var
rd: IXMLDocument;
ns: IDOMNodeList;
n: IDOMNode;
sel: IDOMNodeSelect;
ms: IXMLDOMDocument2;
ms1: IXMLDOMDocument;
i: integer;
s: string;
begin
rd := TXMLDocument.Create(nil);
rd.LoadFromFile(FileName);
if Supports(rd.DocumentElement.DOMNode,
IDOMNodeSelect, sel) then
begin
ms1 := (rd.DOMDocument as TMSDOMDocument).MSDocument;
if Supports(ms1, IXMLDOMDocument2, ms) then begin
ms.setProperty('SelectionNamespaces',
'xmlns="urn:schemas-microsoft-com:office:spreadsheet" '+
'xmlns:o="urn:schemas-microsoft-com:office:office" '+
'xmlns:x="urn:schemas-microsoft-com:office:excel" '+
'xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"');
ms.setProperty('SelectionLanguage', 'XPath');
end;
// ns := sel.selectNodes('/Workbook/Worksheet/@ss:Name/text()');
// ns := sel.selectNodes('/Workbook/Worksheet/@Name/text()');
ns := sel.selectNodes('/Workbook/Worksheet/@ss:Name');
// ns := sel.selectNodes('/Workbook/Worksheet/@Name');
// ns := sel.selectNodes('/Workbook/Worksheet');
for i := 0 to ns.length - 1 do
begin
n := ns.item[i];
s := n.nodeValue;
ShowMessage(s);
end;
end;
end;
Когда я использую упрощенный вниз '/Workbook/Worksheet'
запрос MSXML правильно вернуть узлы. Но как только я добавляю атрибут в запрос - MSXML возвращает пустой набор.
Другие реализации XPath, такие как XMLPad Pro или http://www.freeformatter.com/xpath-tester.html, правильно возвращают список узлов ss:Name
. Но MSXML этого не делает.
Каким будет текст запроса XPath, чтобы помочь MSXML вернуть узлы атрибута с заданными именами?
UPD. @koblik предложил ссылку на селектор MS.NET (не MSXML один), и есть два примера есть https://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx
- Пример 1:
book[@style]
- Все элементы с атрибутами стиля, текущего контекста. - Пример 2:
book/@style
- Атрибут стиля для всех элементов текущего контекста.
Это различие я сказал в «Примечание» выше: не нужны эти book
с, мне нужны style
с. Мне нужны атрибутные узлы, а не узлы-элементы! И этот синтаксис примера 2 - это то, что MSXML, похоже, терпит неудачу.
UPD.2: Один тестер показывает интересную претензию об ошибке: по умолчанию (без префикса) URI пространства имен для запросов XPath всегда «» и не может быть переопределен в «урну: схемы-Microsoft-ком: office: spreadsheet ' Интересно, не претендует ли на то, что претензии в пространстве имен по умолчанию в XPath действительно являются частью стандартного или просто ограничения реализации MSXML.
Затем, если удалить значение по умолчанию NS результаты являются, как они должны быть: Вариант 1: Вариант 2:
Интересно, что утверждение о пространствах имен не по умолчанию в XPath действительно часть стандартное или просто ограничение реализации MSXML.
UPD.3: Мартин Хоннен в комментариях объясняет, что строка: см. W3.org/TR/xpath/#node-tests для XPath 1.0 (поддерживается Microsoft MSXML), в нем четко указано «QName в тесте узла расшифровывается в расширенное имя, используя объявления пространства имен из контекста выражения. Точно так же выполняется расширение для имен типов элементов в начальных и конечных тегах, за исключением того, что пространство имен по умолчанию, объявленное с помощью xmlns, не используется: если QName делает не имеют префикса, тогда URI пространства имен имеет значение null ". Таким образом, в XPath 1.0 такой путь, как «/ Workbook/Worksheet», выбирает элементы этого имени без пространства имен.
UPD.4: Таким образом, выбор работает с запросом XPath '/ss:Workbook/ss:Worksheet/@ss:Name'
, возвращая «ss: Name» атрибуты node directy. В исходном XML-документе оба пространства имен по умолчанию (без префикса) и "ss:" привязаны к одному и тому же URI. Этот URI подтверждается движком XPath. Но не пространство имен по умолчанию, которое не может быть переопределено в MSXML XPath engine (реализация спецификаций 1.0). Чтобы заставить его работать, пространство имен по умолчанию должно быть сопоставлено с другим явным префиксом (либо уже существующим, либо вновь созданным) через URI, а затем этот заменяющий префикс будет использоваться в строке выбора XPath. Поскольку сопоставление пространств имен проходит через URI не через префиксы, было бы неважно, будут ли префиксы, используемые в документе и в совпадении запроса, или нет, они будут сравниваться через их URI.
ms.setProperty('SelectionLanguage', 'XPath');
ms.setProperty('SelectionNamespaces',
'xmlns:AnyPrefix="urn:schemas-microsoft-com:office:spreadsheet"');
, а затем
ns := sel.selectNodes(
'/AnyPrefix:Workbook/AnyPrefix:Worksheet/@AnyPrefix:Name');
Благодаря Asbjorn и Мартин Honnen для объяснения этих тривиальных после-фактум, но не очевидно, априорные отношения.
Что-то вроде '* [@ Name]' или '* [@ ss: Name]'. см. https://msdn.microsoft.com/en-us/library/ms256086(v=vs.110).aspx – kobik
'[@ss: Name]' отлично работает для меня, как упоминает @kobik. –
Что именно возвращается? Список узлов «Worksheet» (неправильно!) или список узлов 'ss: Name' (правильный)? Вот в чем разница. Я сделал обновление и повторил то, что я сказал выше. –