Delphi XML递归函数,搜索具有特定属性值的节点
我试图找到一个具有特定“节点名称”、“属性”和“属性值”的节点。我使用下面的递归函数 我的XMLDocument有一个名为“TestNodeName”的节点,其属性为“Format”,值为“1” 该函数第一次工作正常:返回电源节点。Delphi XML递归函数,搜索具有特定属性值的节点,xml,delphi,recursion,find,Xml,Delphi,Recursion,Find,我试图找到一个具有特定“节点名称”、“属性”和“属性值”的节点。我使用下面的递归函数 我的XMLDocument有一个名为“TestNodeName”的节点,其属性为“Format”,值为“1” 该函数第一次工作正常:返回电源节点。 当我第二次调用它时,它给出了错误的结果:返回一个具有值为0的Format属性的节点 XML示例 <mnode> <TestNodeName ID="1" Format="0"> </TestNodeName> <
当我第二次调用它时,它给出了错误的结果:返回一个具有值为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.
单元4;
接口
使用
窗口、消息、系统工具、变体、类、图形、控件、窗体、,
对话框,XMLIntf,XMLDoc;
类型
TForm4=类(TForm)
过程表单创建(发送方:ToObject);
私有的
{私有声明}
公众的
{公开声明}
结束;
//全局变量
变量
表格4:TForm4;
XML:ixml文档;
mnode:IXMLNode;
s:字符串;
实施
{$R*.dfm}
函数RecursiveFindNode(阳极:IXMLNode;常量SearchNodeName:string):IXMLNode;
变量
I:整数;
开始
如果CompareText(阳极.节点名称,搜索节点名称)=0,则
结果:=阳极
否则,如果未分配(阳极.ChildNodes),则
结果:=零
否则开始
对于I:=0到阳极.ChildNodes.Count-1 do
开始
结果:=RecursiveFindNode(阳极.ChildNodes[I],SearchNodeName);
如果分配(结果),则
出口
结束;
结束;
结束;
函数RecursiveFindNodeAttr(阳极:IXMLNode;常量SearchNodeName:string;sAttr,sAttrVal:string):IXMLNode;
变量
I:整数;
sAttrFind:ixmlnode;
stext:字符串;
开始
sAttrFind:=阳极.属性节点.查找节点(sAttr);
如果是sAttrFindnil,则stext:=sAttrFind.Text,否则stext:='';
如果(CompareText(anox.NodeName,SearchNodeName)=0)和(CompareText(sAttrFind.NodeName,sAttr)=0)和(CompareText(stext,sAttrVal)=0),则
开始
结果:=阳极;
结束
否则,如果未分配(阳极.ChildNodes),则
开始
结果:=无;
结束
否则开始
对于I:=0到阳极.ChildNodes.Count-1 do
开始
结果:=RecursiveFindNodeAttr(阳极.ChildNodes[I],SearchNodeName,sAttr,sAttrVal);
如果分配(结果),则
开始
出口
结束;
结束;
结束;
结束;
程序TForm4.FormCreate(发送方:TObject);
变量
cnode,foundNode:IXMLNode// 似乎“如果没有分配(阳极.ChildNodes),那么”总是被分配的。当我把它改为“if not(anox.HasChildNodes)then”时,它永远停止了循环。我在函数消息中添加了一些内容,以显示函数的执行情况
它似乎对同一个节点进行了2次检查,第一次是在“if(CompareText(aneor.node…”中,第二次是在循环中”,检查I:=0到aneor.ChildNo…”
var
XML:ixml文档;
mnode,cnode:IXMLNode;
开始
XML:=NewXMLDocument;
LoadFromFile('C:\test.XML');
mnode:=XML.DocumentElement;
//这将把所有Format=“1”替换为Format=“0”
cnode:=RecursiveFindNodeAttr(mnode,'TestNodeName','Format','1');
而克诺德尼呢
开始
cnode.Attributes['Format']:=“0”;
ShowMessage(cnode.Attributes['UID']);
cnode:=RecursiveFindNodeAttr(mnode,'TestNodeName','Format','1');
结束;
如果cnode=零,则
ShowMessage('cnode=nil');
结束。
我发现已经生成了一个函数,可以与IXMLDocument一起工作。如果参数匹配,该函数将返回节点列表。XML与上面相同(具有“UID”属性的那个)
“它给出了错误的结果”对我们没有用。您需要提供输入XML、传递给函数的参数、预期结果和实际结果。请记住,我们看不到您的屏幕。请编辑问题以提供缺少的详细信息。当我在程序中第二次调用它时,它会给出错误的结果。即使属性值不是“1”,它也会返回节点(检查上面的调用示例),但它应该返回“nil”。请在问题中,而不是在评论中包含我描述的所有信息。学习如何正确提问将使您实现自给自足。@Nafalem我编辑了您的问题以使其更清晰,但您仍应编辑它并添加XML。@Nafalem“XPath对我来说是一个未知的术语”是一个错误的短语。正确的短语“XPath是一个学习有效实践并变得更具知识性的新机会”以及“我不知道如何使用它”是一个错误的短语,正确的短语是“我已经阅读了维基百科关于XPath的文章,现在我正在谷歌上搜索XPath教程和XPath在线测试站点”
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;
<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.
//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.