2013-07-08 4 views
0

Я пытаюсь найти узел с конкретными «именем узла», «атрибутом» и «значением атрибута». Я использую рекурсивную функцию ниже.Delphi XML Рекурсивная функция, поиск узла с определенным значением атрибута

В моем XMLDocument есть узел с именем «TestNodeName» с атрибутом «Формат» со значением «1».

Эта функция работает нормально в первый раз: возвращает узел прокрутки.
Когда я его вызываю во второй раз, он дает неверные результаты: возвращает узел с атрибутом Format со значением 0.

Пример 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)

Я нашел еще одну проблему. Когда я использую цикл RecursiveFindNodeAttr взамен, замените все format = "1 «to format =« 5 », результат« 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; 
+3

"Это дает неправильные результаты" нет смысла нам. Вам необходимо предоставить входной XML, параметры, которые вы передаете функции, ожидаемый результат и фактический результат. Помните, что мы не видим ваш экран. Пожалуйста, отредактируйте вопрос, чтобы указать недостающую деталь. –

+0

Это дает неправильные результаты, когда я называю это второй раз в программе.Он возвращает значение атрибута узла даже не «1» (проверьте пример вызова выше), но он должен вернуть «nil». – Nafalem

+1

Пожалуйста, включите в вопрос, а не комментарии, всю информацию, которую я описал. Изучение того, как правильно задавать вопрос, проделает долгий путь к самообеспечению. –

ответ

0

Кажется, «если не назначено (ANode.ChildNodes), тогда« всегда назначается. когда я изменил его на «if not (ANode.HasChildNodes)», то «он остановил цикл навсегда. Я добавил в функциональные сообщения, чтобы показать, как точно исполняется функция.

Кажется, он проверяет тот же узел 2 раза, первый время в "если (CompareText (ANode.Node ..." и второй раз в цикле. "Для I: = 0 до 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. 
0

я нашел уже сделал функцию, которая отлично работает с 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. 
Смежные вопросы