Как указать пространство имен при запросе узлов с XPath?
Укороченная версия
Вы делаете это в.NET с:
XmlNode.SelectNodes(query, selectionNamespaces);
Можете ли вы сделать это в JavaScript?
Вы можете сделать это в msxml?
Попытка А:
IXMLDOMNode.selectNodes(query); //no namespaces option
Попытка Б:
IXMLDOMNode.ownerDocument.setProperty("SelectionNamespaces", selectionNamespaces);
IXMLDOMNode.selectNodes(query); //doesn't work
Попытка C:
IXMLDOMDocument3 doc;
doc.setProperty("SelectionNamespaces", selectionNamespaces);
IXMLDOMNodeList list = doc.selectNodes(...)[0].selectNodes(query); //doesn't work
Длинная версия
Дан IXMLDOMNode, содержащий фрагмент xml:
<row>
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
</row>
Мы можем использовать метод IXMLDOMNode.selectNodes для выбора дочерних элементов:
IXMLDOMNode row = //...xml above
IXMLDOMNodeList cells = row.selectNodes("/row/cell");
и это вернет IXMLDOMNodeList:
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
И это нормально.
Но пространства имен ломают это
Если фрагмент XML возник из документа с пространством имен, например:
<row xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
</row>
Тот же запрос XPath ничего не даст, потому что элементы row
а также cell
не существует; они находятся в другом пространстве имен.
Запрос документов с пространством имен по умолчанию
Если бы у вас был полный IXMLDOMDocument, вы бы использовали метод setProperty для установки пространства имен выбора:
а б в
Вы бы запросили пространство имен по умолчанию, дав ему имя, например:
- До:
xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
- После:
xmlns:peanut="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
и тогда вы можете запросить его:
IXMLDOMDocument3 doc = //...document xml above
doc.setProperty("SelectionNamespaces", "xmlns:peanut="http://schemas.openxmlformats.org/spreadsheetml/2006/main");
IXMLDOMNodeList cells = doc.selectNodes("/peanut:row/peanut:cell");
и вы получите свои клетки:
<cell>a</cell>
<cell>b</cell>
<cell>c</cell>
Но это не работает для узла
IXMLDOMNode имеет метод для выполнения запросов XPath:
Метод selectNodes
Применяет указанную операцию сопоставления с шаблоном к контексту этого узла и возвращает список совпадающих узлов как
IXMLDOMNodeList
,HRESULT selectNodes( BSTR expression, IXMLDOMNodeList **resultList);
замечания
Для получения дополнительной информации об использовании
selectNodes
метод с пространствами имен, см. тему метода setProperty.
Но невозможно указать пространства имен выбора при отправке запроса XPath к узлу DOM.
Как я могу указать пространство имен при запросе узлов с XPath?
Решение.NET
.NET XmlNode предоставляет метод SelectNodes, который принимает XmlNamespaceManager
параметр:
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("peanut", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
cells = row.SelectNodes("/peanut:row/peanut:cell", ns);
Но я не на C# (и я не в Javascript). Что такое нативный эквивалент msxml6?
Изменить: я не так много с Javascript ( jsFiddle)
Завершите минимальный пример
program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, msxml, ActiveX;
procedure Main;
var
s: string;
doc: DOMDocument60;
rows: IXMLDOMNodeList;
row: IXMLDOMElement;
cells: IXMLDOMNodeList;
begin
s :=
'<?xml version="1.0" encoding="UTF-16" standalone="yes"?>'+#13#10+
'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'+#13#10+
'<row>'+#13#10+
' <cell>a</cell>'+#13#10+
' <cell>b</cell>'+#13#10+
' <cell>c</cell>'+#13#10+
'</row>'+#13#10+
'</worksheet>';
doc := CoDOMDocument60.Create;
doc.loadXML(s);
if doc.parseError.errorCode <> 0 then
raise Exception.CreateFmt('Parse error: %s', [doc.parseError.reason]);
doc.setProperty('SelectionNamespaces', 'xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main"');
//Query for all the rows
rows := doc.selectNodes('/ss:worksheet/ss:row');
if rows.length = 0 then
raise Exception.Create('Could not find any rows');
//Do stuff with the first row
row := rows[0] as IXMLDOMElement;
//Get the cells in the row
(row.ownerDocument as IXMLDOMDocument3).setProperty('SelectionNamespaces', 'xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main"');
cells := row.selectNodes('/ss:row/ss:cell');
if cells.length <> 3 then
raise Exception.CreateFmt('Did not find 3 cells in the first row (%d)', [cells.length]);
end;
begin
try
CoInitialize(nil);
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
1 ответ
Это ответ на MSDN:
Как указать пространство имен при запросе DOM с XPath
Обновить:
Обратите внимание, однако, что во втором примере XML <row>
а также <cell>
элементы НЕ находятся в пространстве имен, запрашиваемом XPath при добавлении xmlns:peanut
к SelectionNamespaces
имущество. Вот почему <cell>
элементы не найдены.
Чтобы правильно поместить их в пространство имен, вам необходимо:
изменить объявление пространства имен для использования
xmlns=
вместоxmlns:ss=
:<row xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <cell>a</cell> <cell>b</cell> <cell>c</cell> </row>
использование
<ss:row>
а также<ss:cell>
вместо<row>
а также<cell>
:<ss:row xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <ss:cell>a</cell> <ss:cell>b</cell> <ss:cell>c</cell> </ss:row>
SelectionNamespaces
Свойство магически не помещает элементы в пространство имен для вас, оно только указывает, какие пространства имен доступны для использования запроса XPath. Сам XML должен помещать элементы в соответствующие пространства имен по мере необходимости.
Обновить:
В вашем новом примере cells := row.selectNodes('/ss:row/ss:cell');
не работает, потому что запрос XPath использует абсолютный путь, где ведущий /
начинается с корня документа, и нет <row>
элементы в верхней части документа XML, только <worksheet>
элемент. Поэтому rows := doc.selectNodes('/ss:worksheet/ss:row');
работает.
Если вы хотите выполнить запрос XPath, который начинается с запрашиваемого узла, не используйте абсолютный путь, вместо этого используйте относительный путь:
cells := row.selectNodes('ss:row/ss:cell');
Или просто:
cells := row.selectNodes('ss:cell');