如何从XPath选择中获取IXMLNodeList?

时间:2014-04-28 11:19:46

标签: xml delphi xpath delphi-xe2

我遇到了a question about XPath and Delphi TXmlDocument

虽然答案适用于选择单个xml节点,但我想用它来选择节点列表。

我发现similar utility function应该完全正确,但它无法正常工作。

明显有缺陷的功能:

uses
  Xml.Xmldom, Xml.XMLIntf, Xml.XMLDoc;

function SelectNodes(xnRoot: IXmlNode; const nodePath: WideString): IXMLNodeList;
var
  intfSelect : IDomNodeSelect;
  intfAccess : IXmlNodeAccess;
  dnlResult  : IDomNodeList;
  intfDocAccess : IXmlDocumentAccess;
  doc: TXmlDocument;
  i : Integer;
  dn : IDomNode;
begin
  Result := nil;
  if not Assigned(xnRoot)
    or not Supports(xnRoot, IXmlNodeAccess, intfAccess)
    or not Supports(xnRoot.DOMNode, IDomNodeSelect, intfSelect) then
    Exit;

  dnlResult := intfSelect.selectNodes(nodePath);
  if Assigned(dnlResult) then
  begin
    Result := TXmlNodeList.Create(intfAccess.GetNodeObject, '', nil);
    if Supports(xnRoot.OwnerDocument, IXmlDocumentAccess, intfDocAccess) then
      doc := intfDocAccess.DocumentObject
    else
      doc := nil;

    for i := 0 to dnlResult.length - 1 do
    begin
      dn := dnlResult.item[i];
      Result.Add(TXmlNode.Create(dn, nil, doc));
    end;
  end;
end;

不使用IXMLNodeList的简化版本,但" raw" IDomNodeList代替:

function SimpleSelectNodes(xnRoot: IXmlNode; const nodePath: WideString): IDOMNodeList;
var
  intfSelect : IDomNodeSelect;
begin
  Result := nil;
  if not Assigned(xnRoot)
    or not Supports(xnRoot.DOMNode, IDomNodeSelect, intfSelect) then
    Exit;

  Result := intfSelect.selectNodes(nodePath);
end;

测试代码:

procedure TForm1.FormCreate(Sender: TObject);
var
  Doc: IXMLDocument;
  Root: IXMLNode;
  DomNodeList: IDomNodeList;
  XmlNodeList: IXMLNodeList;
  XmlNode : IXMLNode;
  I: Integer;
begin
  // Build a test DOM tree in memory
  Doc := NewXMLDocument;
  Root := Doc.AddChild('root');
  Root.AddChild('C1');
  Root.AddChild('C2');
  Root.AddChild('C3');

  // Select using the IDomNodeList interface
  DomNodeList := SimpleSelectNodes(Root, '/root/*');
  for I := 0 to DomNodeList.length - 1 do
    ShowMessage(DomNodeList.item[I].nodeName);

  // Select using the IXMLNodeList interface
  XmlNodeList := SelectNodes(Root, '/root/*');
  XmlNode := XmlNodeList.First;
  while XmlNode <> nil do
  begin
    ShowMessage(XmlNode.NodeName);
    XmlNode := XmlNode.NextSibling;
  end;
end;

虽然SimpleSelectNodes版本工作正常,但SelectNodes功能却没有。

它返回IXMLNodeList,但当我尝试实际迭代此列表时,我只获得第一项,NextSiblingnil

如何让IXMLNodeList工作?

1 个答案:

答案 0 :(得分:2)

我遇到了同样的问题(代码基本相同)。

我能解决它的唯一方法是按索引遍历节点,因为NextSibling每次都因某种原因返回nil。这样的事情有效:

var
  i: Integer;
  Nodes: IXMLNodeList;
  Node: IXMLNode;
begin
  Nodes := SelectNodes(...);
  for i := 0 to NodeList.Count - 1 do
  begin
    Node := NodeList.Nodes[i];
    // Process node
  end;
end;