I'm new to delphi and now I have to read create an xml. my code is the following:
function foo.createXMLDocument(): TXMLDocument;
var
res: TXMLDocument;
rootNode: IXMLNode;
sl : TStringList;
begin
res := TXMLDocument.Create(nil);
res.Active := true;
rootNode := res.AddChild('label');
// create string for debug purposes
sl := TStringList.Create;
sl.Assign(res.XML);// sl is empty after this assignment
//add more elements
generateDOM(rootNode);
Result := res;
end;
The problem is, the count of child nodes increases but res.XML is empty. Not to mention that the rest of elements in the generateDOM procedure doesn't seem to be doing anything. I will be very glad with your help.
Disclaimer: Tested with D2007.
Your code does indeed create the XML (<label/>
) as shown in this modified function:
function createXMLDocument(): TXMLDocument;
var
res: TXMLDocument;
rootNode: IXMLNode;
sl : TStringList;
begin
res := TXMLDocument.Create(nil);
res.Active := true;
rootNode := res.AddChild('label');
// create string for debug purposes
sl := TStringList.Create; // not needed
sl.Assign(res.XML); // Not true: sl is empty after this assignment
ShowMessage(sl.text);// sl is NOT empty!
sl.Free; // don't forget to free it! use try..finally.. to guarantee it!
//add more elements
// generateDOM(rootNode);
Result := res;
end;
But it calls for a lot of remarks:
- You don't need a local res variable, just use the Result.
- You don't need an extra StringList to see the XML: Result.Xml.Text
- Don't forget to Free the sl StringList if you create one.
- The XmlDocument you return is unusable outside the function and gives an AV if you try.
Why?
It's because an XMLDocument is intended to be used as a Component with an Owner, or as an Interface otherwise, in order to manage its lifetime.
The fact that you use an Interface to hold rootNode causes it to be destroyed at the end of the CreateXmlDocument function. And if you look at the code in TXMLNode._Release
, you'll see that it triggers TXMLDocument._Release
which calls Destroy unless there is an Owner for the XMLDocument (or an interface holding a reference to it).
This is why the XMLDocument is valid and populated inside the CreateXMLDocument function but not available outside it unless you return an Interface or provide an Owner.
See the alternate solutions below:
function createXMLDocumentWithOwner(AOwner: TComponent): TXMLDocument;
var
rootNode: IXMLNode;
begin
Assert(AOwner <> nil, 'createXMLDocumentWithOwner cannot accept a nil Owner');
Result := TXMLDocument.Create(AOwner);
Result.Active := True;
rootNode := Result.AddChild('label');
OutputDebugString(PChar(Result.Xml.Text));
//add more elements
// generateDOM(rootNode);
end;
function createXMLDocumentInterface(): IXMLDocument;
var
rootNode: IXMLNode;
begin
Result := TXMLDocument.Create(nil);
Result.Active := True;
rootNode := Result.AddChild('label');
OutputDebugString(PChar(Result.Xml.Text));
//add more elements
// generateDOM(rootNode);
end;
procedure TForm7.Button1Click(Sender: TObject);
var
doc: TXmlDocument;
doc2: IXMLDocument;
begin
ReportMemoryLeaksOnShutdown := True;
doc := createXMLDocument;
// ShowMessage( doc.XML.Text ); // cannot use it => AV !!!!
// already freed, cannot call doc.Free;
doc := createXMLDocumentWithOwner(self);
ShowMessage( doc.XML.Text );
doc2 := createXMLDocumentInterface;
ShowMessage( doc2.XML.Text );
end;