2013-07-08 3 views
0

특정 "노드 이름", "속성"및 "속성 값"이있는 노드를 찾으려고합니다. 아래의 재귀 함수를 사용합니다.Delphi XML 재귀 함수, 특정 속성 값을 가진 노드 검색

내 XMLDocument의 값이 '1'인 'Format'속성을 가진 'TestNodeName'노드가 있습니다.

함수는 처음에는 잘 작동합니다. 전달 노드를 반환합니다.
두 번째로 호출하면 잘못된 결과가 나타납니다. 값이 0 인 Format 특성을 가진 노드를 반환합니다.

XML 예제입니다. 몇 가지 테스트 후 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. 

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

끝 나는 마지막으로 기능의 잘못된 결과의 원인을 발견했다. 유사한 재귀 함수가있었습니다. 함수의 호출을 제거 할 때 모든 결과는 괜찮습니다. RecursiveFindNode (ANode : IXMLNode; const SearchNodeName : string) : IXMLNode;

코드에서 다음 코드를 호출합니다. foundNode : = RecursiveFindNode (mnode, 'TestNodeName'); RecursiveFindNodeAttr의 첫 번째 통화로 인해 양호한 결과를 제공한다 "cnode를 ="나는 동일한 변수를 사용하기 때문에 RecursiveFindNodeAttr의 두 번째 호출 (ID = 1) 잘못된 결과를 제공한다 :

"foundNode = RecursiveFindNodeAttr를 (..." 난 "var에 foundNode : IXMLNode을 「이동 마지막

글로벌 번째 호출 Tform4 선언에서 좋은 결과를 리턴 (ID = 4) I 다른 문제 발견

나 루프 RecursiveFindNodeAttr 사용할 때 결국 포맷을 대체 ="1. "format ="5 "로 설정하면"foundNode "결과가"nil "이 아니므로 루프가 끝나지 않습니다.

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

"Assigned (ANode.ChildNodes) then then"이 항상 할당되어있는 것처럼 보입니다. 내가 그것을 "(ANode.HasChildNodes)가 아니라면"으로 변경하면 루프가 영원히 멈췄다. 함수 메시지가 정확히 어떻게 함수가 실행되는지 표시하기 위해 추가했습니다.

루프의 두 번째 "(CompareText (ANode.Node ... 경우"동일 노드 2 회, 1 일의 시간을 확인 보인다. "의 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

I 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. 
관련 문제