Проблема построения XML-документа с использованием TXMLDocument
Я новичок в Delphi, и теперь я должен прочитать создать XML. мой код следующий:
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;
Проблема в том, что количество дочерних узлов увеличивается, но res.XML пуст. Не говоря уже о том, что остальные элементы процедуры generateDOM, похоже, ничего не делают. Я буду очень рад с вашей помощью.
3 ответа
Отказ от ответственности: протестировано с D2007.
Ваш код действительно создает XML (<label/>
), как показано в этой модифицированной функции:
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;
Но это требует много замечаний:
- Вам не нужна локальная переменная res, просто используйте Result.
- Вам не нужен дополнительный StringList для просмотра XML: Result.Xml.Text
- Не забудьте освободить sl StringList, если вы его создадите.
- Возвращаемый вами XmlDocument вне функции и дает AV, если вы попытаетесь.
Зачем?
Это потому, что XMLDocument предназначен для использования в качестве Компонента с Владельцем или в качестве Интерфейса иным образом для управления его временем жизни.
Тот факт, что вы используете интерфейс для хранения rootNode, приводит к его уничтожению в конце функции CreateXmlDocument. И если вы посмотрите на код в TXMLNode._Release
вы увидите, что это вызывает TXMLDocument._Release
который вызывает Destroy, если нет владельца для XMLDocument (или интерфейса, содержащего ссылку на него).
Вот почему XMLDocument является действительным и заполняется внутри функции CreateXMLDocument, но не доступен вне ее, если вы не вернете интерфейс или не предоставите владельца.
Смотрите альтернативные решения ниже:
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;
Справка Delphi по методу TXMLDocument.AddChild сообщает (внизу):
Примечание. Не вызывайте AddChild, чтобы добавить дочерний элемент в элемент документа этого документа. При добавлении данных в документ XML используйте метод AddChild элемента документа или узла в иерархии, который должен быть родительским для нового узла.
И это то, что вы делаете правильно?:-)
Вот вводная статья о программировании документов на Delphi XML и показано, как вы можете работать со свойством TXMLDocument.DocumentElement вместо определения переменной rootnode в своем коде.
В моей аналогичной реализации я объявляю res как IXMLDocument вместо TXMLDocument.
var
XMLDoc: IXMLDocument;
.
.
XMLDoc := TXMLDocument.Create(nil);
XMLDoc.Active := True;
.
.
XMLDoc.SaveToFile(Filename);
XMLDoc.Active := False;