Issue building an XML document using TXMLDocument
Asked Answered
P

3

8

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.

Patriarchate answered 7/10, 2009 at 15:27 Comment(1)
Would be good if you provided the Delphi version you're using. See my answer in case of D2007.Aloisius
A
12

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;
Aloisius answered 8/10, 2009 at 0:32 Comment(0)
I
4

The Delphi Help of TXMLDocument.AddChild method says (at the bottom):

Note: Do not call AddChild to add a child to the document element of this document. When adding data to the XML document, use the AddChild method of the document element or of the node in the hierarchy that should be the parent of the new node.

And this is what you are doing right? :-)

Here is an introduction article about Delphi XML Document Programming and shows how you can work with the TXMLDocument.DocumentElement property instead of your definition of the rootnode variable in your code.

Ioyal answered 7/10, 2009 at 17:4 Comment(4)
The introduction you cite does not demonstrate how to create a new document from scratch. Neither does the help page. How should the code in the question change to make it right?Guard
in D2007 TXmlDocument.AddChild creates the DocumentNode if not already there and calls its AddChild. So it's not the problem.Aloisius
Erwin, you editing your answer, but I don't think you addressed my comment.Guard
This was not exactly what I was looking for but the link to the help was extremely useful since I only have access to the german version of the delphi help and I was looking the english version online.Patriarchate
A
2

In my similar implementation, I declare res as IXMLDocument instead of TXMLDocument.

var
   XMLDoc: IXMLDocument;
.
.
   XMLDoc := TXMLDocument.Create(nil);
   XMLDoc.Active := True;
.
.
   XMLDoc.SaveToFile(Filename);
   XMLDoc.Active := False;
Accompaniment answered 7/10, 2009 at 16:2 Comment(1)
Although that is the correct thing to do when instantiating a TXmlDocument without an owner component, it's irrelevant to the problem at hand.Guard

© 2022 - 2024 — McMap. All rights reserved.