0

我尝试找到具有特定“节点名称”、“属性”和“属性值”的节点。我使用下面的递归函数。

我的 XMLDocument 有一个名为“TestNodeName”的节点,其属性为“Format”,值为“1”。

该函数第一次运行良好:返回更正确的节点。
当我第二次调用它时,它给出了错误的结果:返回一个具有值为 0 的 Format 属性的节点。

XML 示例。

<mnode>
  <TestNodeName ID="1" Format="0">
  </TestNodeName>
  <TestNodeName ID="2" Format="1">
  </TestNodeName>
  <TestNodeName ID="3" Format="0">
  </TestNodeName>
  <TestNodeName ID="4" Format="1">
  </TestNodeName>
  <TestNodeName ID="5" Format="0">
  </TestNodeName>
</mnode>

XML 结束

unit Unit4;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, XMLIntf, XMLDoc;

type
  TForm4 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
//GLOBAL VARIABLES
var
  Form4: TForm4;
  XML:IXMLDocument;
  mnode:IXMLNode;
  s:string;
implementation

{$R *.dfm}
function RecursiveFindNode(ANode: IXMLNode; const SearchNodeName: string): IXMLNode;
var
  I: Integer;
begin
  if CompareText(ANode.NodeName, SearchNodeName) = 0 then
    Result := ANode
  else if not Assigned(ANode.ChildNodes) then
    Result := nil
  else begin
    for I := 0 to ANode.ChildNodes.Count - 1 do
    begin
      Result := RecursiveFindNode(ANode.ChildNodes[I], SearchNodeName);
      if Assigned(Result) then
        Exit;
    end;
  end;
end;

function RecursiveFindNodeAttr(ANode: IXMLNode; const SearchNodeName: string; sAttr, sAttrVal:string): IXMLNode;
var
  I: Integer;
  sAttrFind: ixmlnode;
  stext:string;
begin
  sAttrFind:=ANode.AttributeNodes.FindNode(sAttr);
  if sAttrFind<>nil then stext:=sAttrFind.Text else stext:='';
  if (CompareText(ANode.NodeName, SearchNodeName)=0)and(CompareText(sAttrFind.NodeName, sAttr)=0)and(CompareText(stext, sAttrVal)=0) then
  begin
    Result := ANode;
  end
  else if not Assigned(ANode.ChildNodes) then
  begin
    Result := nil;
  end
  else begin
    for I := 0 to ANode.ChildNodes.Count - 1 do
    begin
      Result := RecursiveFindNodeAttr(ANode.ChildNodes[I], SearchNodeName, sAttr, sAttrVal);
      if Assigned(Result) then
      begin
        Exit;
      end;
    end;
  end;
end;

procedure TForm4.FormCreate(Sender: TObject);
var
cnode,foundNode:IXMLNode; //<-- Problem here "foundNode" must be in global 
begin
XML:= NewXMLDocument;
XML.LoadFromFile('C:\test.xml');
mnode:=XML.DocumentElement;
foundNode:=RecursiveFindNode(mnode,'TestNodeName');

//First time
cnode:=RecursiveFindNodeAttr(XML.DocumentElement,'TestNodeName','Format','1');
if cnode<>nil then
begin
cnode.Attributes['Format']:='5';
ShowMessage('ID='+cnode.Attributes['ID']);
end
else
ShowMessage('nil');

//Second time
foundNode:=RecursiveFindNodeAttr(XML.DocumentElement,'TestNodeName','Format','1');
if foundNode<>nil then
begin
foundNode.Attributes['Format']:='5';
ShowMessage('ID='+foundNode.Attributes['ID']);
end
else
ShowMessage('nil');

XML.SaveToFile('C:\test.xml');


end;

end.

经过几次测试,我终于找到了导致函数结果错误的原因。有一个类似的递归函数。当我删除函数调用时,所有结果都正常。RecursiveFindNode(ANode: IXMLNode; const SearchNodeName: string): IXMLNode;

在下面的代码中调用 foundNode:=RecursiveFindNode(mnode,'TestNodeName'); 第一次调用 RecursiveFindNodeAttr 会给出好的结果,因为 "cnode:=" 第二次调用 RecursiveFindNodeAttr 会给出错误的结果 (ID=1) 因为我使用了相同的变量 "foundNode:=RecursiveFindNodeAttr(..."

最后,当我移动“var foundNode:IXMLNode;”时 从 Tform4 声明到全局,第二次调用返回良好结果 (ID=4)

我发现了另一个问题。当我在将所有格式=“1”替换为格式=“5”后在循环中使用 RecursiveFindNodeAttr 时,“foundNode”结果仍然是“非零”,因此循环永远不会结束。

foundNode:=RecursiveFindNodeAttr(XML.DocumentElement,'TestNodeName','Format','1');
while foundNode<>nil do
begin
    foundNode.Attributes['Format']:='5';
    ShowMessage('ID='+foundNode.Attributes['ID']);

foundNode:=RecursiveFindNodeAttr(XML.DocumentElement,'TestNodeName','Format','1');
if foundNode=nil then ShowMessage('nil');
end;
4

2 回答 2

3

我发现已经制作了可以与 IXMLDocument 一起正常工作的函数。如果匹配参数,函数返回节点列表。XML与上面相同(具有“UID”属性的那个)

//Declared funciton
function FindNodeList(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;

var
  xlist:IXMLNodeList;
  mnode:IXMLNode;
  XML:IXMLDocument;
begin
  XML:= NewXMLDocument;
  XML.LoadFromFile('C:\test.xml');
  mnode:=XML.DocumentElement;

  //This will find all nodes in nodes and subnodes if node name is "TestNodeName" attribute Format="1"
  xlist:=FindNodeList(mnode,'//TestNodeName[@Format="1"]');
  for I := 0 to xlist.Count - 1 do
    begin
      //This will set Format value to "8"
      xlist[i].Attributes['Format']:='8';
    end;


end.
于 2013-07-10T09:11:56.807 回答
0

似乎总是分配“如果未分配(ANode.ChildNodes)则”。当我将其更改为“如果不是(ANode.HasChildNodes)则”时,它会永远停止循环。我添加到函数消息中以显示函数的执行方式。

似乎它检查了同一个节点 2 次,第一次在“if (CompareText(ANode.Node...”) 中检查,第二次在循环“for I:= 0 to ANode.ChildNo...”中检查。

<mnode UID="main">
  <TestNodeName UID="1" Format="5">
    <TestNodeName UID="11" Format="5">
    </TestNodeName>
    <TestNodeName UID="12" Format="5">
    </TestNodeName>
    <TestNodeName UID="13" Format="5">
    </TestNodeName>
  </TestNodeName>
  <TestNodeName UID="2" Format="5">
  </TestNodeName>
  <TestNodeName UID="3" Format="5">
    <TestNodeName UID="31" Format="5">
    </TestNodeName>
    <TestNodeName UID="32" Format="5">
      <TestNodeName UID="321" Format="1">
      </TestNodeName>
      <TestNodeName UID="322" Format="5">
      </TestNodeName>
      <TestNodeName UID="323" Format="1">
      </TestNodeName>
    </TestNodeName>
    <TestNodeName UID="33" Format="5">
    </TestNodeName>
  </TestNodeName>
  <TestNodeName UID="4" Format="5">
  </TestNodeName>
</mnode>

function RecursiveFindNodeAttr(ANode: IXMLNode; const SearchNodeName: string; sAttr, sAttrVal:string): IXMLNode;
var
  I: Integer;
begin
  if (CompareText(ANode.NodeName, SearchNodeName)=0)and(ANode.Attributes[sAttr]=sAttrVal) then
  begin
    ShowMessage('Found! '+ANode.Attributes['UID']);
    Result := ANode;
  end
  else
    begin
    ShowMessage('1st try='+ANode.Attributes['UID']);
    if not (ANode.HasChildNodes) then
    begin
    ShowMessage('nil');
    Result := nil;
    end
  else begin
    for I := 0 to ANode.ChildNodes.Count - 1 do
     begin
      Result := RecursiveFindNodeAttr(ANode.ChildNodes[I], SearchNodeName, sAttr, sAttrVal);
      ShowMessage('loop='+ANode.ChildNodes[I].Attributes['UID']);
      if Assigned(Result) then
      begin
        ShowMessage('found in loop='+ANode.ChildNodes[I].Attributes['UID']);
        Exit;
      end;
     end;
    end;
   end;
end;

var
  XML:IXMLDocument;
  mnode,cnode:IXMLNode;

begin

XML:= NewXMLDocument;
XML.LoadFromFile('C:\test.xml');
mnode:=XML.DocumentElement;

//This will replace all Format="1" to Format="0"
cnode:=RecursiveFindNodeAttr(mnode,'TestNodeName','Format','1');
while cnode<>nil do
begin
  cnode.Attributes['Format'] := '0';
  ShowMessage(cnode.Attributes['UID']);
  cnode:=RecursiveFindNodeAttr(mnode,'TestNodeName','Format','1');
end;
if cnode= nil  then
ShowMessage('cnode= nil ');

end.
于 2013-07-10T07:36:03.437 回答