[ACCEPTED]-Issue building an XML document using TXMLDocument-txmldocument
Disclaimer: Tested with D2007.
Your code does indeed create the XML (<label/>
) as shown 17 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 16 for a lot of remarks:
- You don't need a local res variable, just 15 use the Result.
- You don't need an extra 14 StringList to see the XML: Result.Xml.Text
- Don't 13 forget to Free the sl StringList if you create 12 one.
- The XmlDocument you return is unusable outside the function and gives an AV if you try.
Why?
It's because an XMLDocument is 11 intended to be used as a Component with an Owner, or as an Interface otherwise, in 10 order to manage its lifetime.
The fact that you 9 use an Interface to hold rootNode causes 8 it to be destroyed at the end of the CreateXmlDocument 7 function. And if you look at the code in 6 TXMLNode._Release
, you'll see that it triggers TXMLDocument._Release
which calls 5 Destroy unless there is an Owner for the 4 XMLDocument (or an interface holding a reference 3 to it).
This is why the XMLDocument is valid 2 and populated inside the CreateXMLDocument 1 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;
The Delphi Help of TXMLDocument.AddChild method says 11 (at the bottom):
Note: Do not call AddChild 10 to add a child to the document element of 9 this document. When adding data to the XML 8 document, use the AddChild method of the 7 document element or of the node in the hierarchy 6 that should be the parent of the new node.
And 5 this is what you are doing right? :-)
Here 4 is an introduction article about Delphi XML Document Programming and shows 3 how you can work with the TXMLDocument.DocumentElement 2 property instead of your definition of the 1 rootnode variable in your code.
In my similar implementation, I declare 1 res as IXMLDocument instead of TXMLDocument.
var
XMLDoc: IXMLDocument;
.
.
XMLDoc := TXMLDocument.Create(nil);
XMLDoc.Active := True;
.
.
XMLDoc.SaveToFile(Filename);
XMLDoc.Active := False;
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.