代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

使用XPath查询节点时如何指定命名空间?

  •  -1
  • Ian Boyd  · 技术社区  · 6 年前

    短版

    尝试

    IXMLDOMNode.selectNodes(query); //no namespaces option
    

    尝试B

    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

    <row>
        <cell>a</cell>
        <cell>b</cell>
        <cell>c</cell>
    </row>
    

    我们可以使用 IXMLDOMNode.selectNodes 选择子元素的方法:

    IXMLDOMNode row = //...xml above
    
    IXMLDOMNodeList cells = row.selectNodes("/row/cell");
    

    :

    • <cell>a</cell>
    • <cell>b</cell>
    • <cell>c</cell>

    但是名称空间破坏了它

    <row xmlns:ss="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
        <cell>a</cell>
        <cell>b</cell>
        <cell>c</cell>
    </row>
    

    同一个XPath查询不会有任何结果,因为元素 row cell 不存在;它们在另一个命名空间中。

    使用默认命名空间查询文档

    如果你有一个完整的 IXMLDOMDocument ,您可以使用 方法来设置 :

    一 b c

    • 以前 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");
    

    你得到了你的手机:

    • <单元格>a</cell>
    • <
    • <单元格>c</cell>

    但这对节点不起作用

    method to perform XPath queries:

    selectNodes方法

    将指定的模式匹配操作应用于此节点的上下文,并将匹配节点列表返回为 IXMLDOMNodeList .

    HRESULT selectNodes(  
          BSTR expression,  
          IXMLDOMNodeList **resultList); 
    

    selectNodes 方法,请参见 setProperty Method

    但是在对DOM节点发出XPath查询时,无法指定选择名称空间。

    使用XPath查询节点时,如何指定命名空间?

    .NET的 提供 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);
    

    编辑 ( 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.
    
    0 回复  |  直到 6 年前
        1
  •  1
  •   Remy Lebeau    6 年前

    How To Specify Namespace when Querying the DOM with XPath

    但是请注意,在第二个示例XML中 <row> <cell> 添加时,XPath查询的命名空间中没有元素 xmlns:peanut SelectionNamespaces 财产。这就是为什么 <单元格>

    要将它们正确地放入名称空间,您必须:

    • 更改命名空间声明以使用 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> 而不是 < < :

      <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>
      

    选择命名空间 属性不会神奇地将元素放入命名空间中,它只指定哪些命名空间可供XPath查询使用。将元素放入需要的XML中。

    更新:

    在你的新例子中, cells := row.selectNodes('/ss:row/ss:cell'); 绝对的 / 从文档根开始,没有 <世界其他地区> <worksheet> rows := doc.selectNodes('/ss:worksheet/ss:row'); 作品。

    相对的 改为路径:

    cells := row.selectNodes('ss:row/ss:cell');
    

    cells := row.selectNodes('ss:cell');