Я хочу попробовать разобрать файл электронной таблицы Excel XML с помощью MSXML и XPath.
- 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 всегда '', и его нельзя переопределить на 'urn: schemas-microsoft- com: 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 пространства имен равен нулю ". Таким образом, в XPath 1.0 такой путь, как «/ Workbook / Worksheet», выбирает элементы с этим именем без пространства имен.
UPD.4: Таким образом, выбор работает с '/ss:Workbook/ss:Worksheet/@ss:Name'
запросом XPath, возвращая непосредственно узлы атрибутов "ss: Name". В исходном XML-документе пространства имен по умолчанию (без префикса) и ss: привязаны к одному и тому же URI. Этот URI подтверждается механизмом XPath. Но не пространство имен по умолчанию, которое нельзя переопределить в движке MSXML XPath (реализующем спецификации 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' );
Спасибо Асбьёрну и Мартину Хоннену за объяснение этих тривиальных постфактум, но не очевидных априорных соотношений.
*[@Name]
или*[@ss:Name]
. см. msdn.microsoft.com/en-us /library/ms256086(v=vs.110).aspx - person kobik   schedule 26.09.2016[@ss:Name]
у меня отлично работает, как упоминает @kobik. - person Ken White   schedule 26.09.2016Worksheet
узлов (неверно!) Или списокss:Name
узлов (правильно)? Вот в чем разница. Я сделал обновление и повторил сказанное выше. - person Arioch 'The   schedule 26.09.2016@ss:Name
. - person Ken White   schedule 26.09.2016SelectionNamespaces
иSelectionLanguage
как XPath, вам понадобится путьns := sel.selectNodes('/ss:Workbook/ss:Worksheet/@ss:Name');
. - person Martin Honnen   schedule 26.09.2016TXMLDocument
(который, если я помню, внутренне использует MSXML) ... stackoverflow.com/questions/30687619/ - person Jerry Dodge   schedule 27.09.2016