1

Hy! I want to add IXMLNodes to an XML document. My main procedure is btnXmlSaveToFileClick which Saves XML structure to a file. My problem is I cannot add IXMLNode as a child. Sorry for my English.

Any help or advice would be greatly appreciated.

Here is my example:

*

unit Unit3;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  XMLDoc, XMLIntf, Vcl.StdCtrls;

type
  IXMLDataSample1 = interface(IXMLNode)
  ['{D7B7E084-1BF8-4185-85D1-1C83B74EA651}']
    function GetXmlDAtaSample1: WideString;
    procedure SetXmlDAtaSample1(const Value: WideString);
    property XmlDataSample1: WideString read GetXmlDataSample1 write SetXmlDataSample1;
  end;

  IXMLDataSample2 = interface(IXMLNode)
  ['{6D7CE38B-3F57-4F18-A337-8405E72637E6}']
    function GetXmlDataSample2: WideString;
    procedure SetXmlDataSample2(const Value: WideString);
    property XmlDataSample2: WideString read GetXmlDataSample2 write SetXmlDataSample2;
  end;

  TXMLHandler<IXMLType: IXMLNode> = class
  private
    class function ConvertXMLNodeToXMLType(aNode: IXMLNode): IXMLType;
  protected
    class function GetXMLClass: TClass; virtual; abstract;
    class function GetXMLRootElementName: WideString; virtual; abstract;
    class function GetXMLIfType: TGUID; virtual; abstract;
    class procedure SaveXMLToFile(aXML: IXMLType; const aFileName: string);
    class function NewXML: IXMLType; virtual;
  end;

  TDataSampleXMLHandler1 = class(TXMLHandler<IXMLDataSample1>)
  public
    class function GetXMLClass: TClass; override;
    class function GetXMLRootElementName: WideString; override;
    class function GetXMLIfType: TGUID; override;
  end;

  TDataSampleXMLHandler12 = class(TXMLHandler<IXMLDataSample2>)
  protected
    class function GetXMLClass: TClass; override;
    class function GetXMLRootElementName: WideString; override;
    class function GetXMLIfType: TGUID; override;
  end;

  TForm3 = class(TForm)
    btnXmlSaveToFile: TButton;
    procedure btnXmlSaveToFileClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

type
  TXMLSampleDataObj1 = class(TXMLNode, IXMLDataSample1)
  private
    function GetXmlDataSample1: WideString;
    procedure SetXmlDataSample1(const Value: WideString);
  end;

  TXMLSampleDataObj2 = class(TXMLNode, IXMLDataSample2)
  private
    function GetXMlDataSample2: WideString;
    procedure SetXmlDAtaSample2(const Value: WideString);
  end;

procedure TForm3.btnXmlSaveToFileClick(Sender: TObject);
 //Works well
  vXML1 := TDataSampleXMLHandler1.NewXML;
  vXML1.XmlDataSample1 := 'test data';
  TDataSampleXMLHandler1.SaveXMLToFile(vXml1, 'C:\Teszt\XmlSample.XML');

  //My problem here, cannot add IXMLNode to XMLDocument
  vXMLDoc := NewXMLDocument;
  vXMLDoc.ChildNodes.Add(vXML1);
  //And add vXML2 and more...
  vXMLDoc.SaveToFile('C:\Teszt\XmlDocSaveSAmple.XML');
end;

{ TXMLHandler<IXMLType> }

class function TXMLHandler<IXMLType>.ConvertXMLNodeToXMLType(
  aNode: IXMLNode): IXMLType;
begin
  Result := default(IXMLType);
  try
    Supports(aNode, GetXMLIfType, result);
  finally
    aNode := nil;
  end;
end;

class function TXMLHandler<IXMLType>.NewXML: IXMLType;
begin
  Result :=
      ConvertXMLNodeToXMLType(NewXMLDocument.GetDocBinding(GetXMLRootElementName, GetXMLClass, ''));
end;

class procedure TXMLHandler<IXMLType>.SaveXMLToFile(aXML: IXMLType;
  const aFileName: string);
begin
  aXML.OwnerDocument.SaveToFile(aFileName);
end;

{ TDataSampleXMLHandler12 }


class function TDataSampleXMLHandler12.GetXMLClass: TClass;
begin
  Result := TXMLSampleDataObj2;
end;

class function TDataSampleXMLHandler12.GetXMLIfType: TGUID;
begin
  Result := IXMLDataSample2;
end;

class function TDataSampleXMLHandler12.GetXMLRootElementName: WideString;
begin
  Result := 'Sample2'
end;

{ TDataSampleXMLHandler1 }

class function TDataSampleXMLHandler1.GetXMLClass: TClass;
begin
  Result := TXMLSampleDataObj1;
end;

class function TDataSampleXMLHandler1.GetXMLIfType: TGUID;
begin
  Result := IXMLDataSample1;
end;

class function TDataSampleXMLHandler1.GetXMLRootElementName: WideString;
begin
  Result := 'Sample2';
end;

{ TXMLSampleDataObj1 }

function TXMLSampleDataObj1.GetXmlDataSample1: WideString;
begin
  Result := ChildNodes['XmlDataSample1'].Text;
end;

procedure TXMLSampleDataObj1.SetXmlDataSample1(const Value: WideString);
begin
  ChildNodes['XmlDataSample1'].NodeValue := Value;
end;

{ TXMLSampleDataObj2 }

function TXMLSampleDataObj2.GetXMLDataSample2: WideString;
begin
  Result := ChildNodes['XmlDataSample2'].Text;
end;

procedure TXMLSampleDataObj2.SetXmlDataSample2(const Value: WideString);
begin
  ChildNodes['XmlDataSample2'].NodeValue := Value;
end;

end.*

Saving a single XML node to a document is working well. But I want to add these nodes to an XML document the application stoppes, got no error. My problem here is:

 procedure TForm3.btnXmlSaveToFileClick(Sender: TObject);
 //Works well
  vXML1 := TDataSampleXMLHandler1.NewXML;
  vXML1.XmlDataSample1 := 'test data';
  TDataSampleXMLHandler1.SaveXMLToFile(vXml1, 'C:\Teszt\XmlSample.XML');

  //My problem here, cannot add IXMLNode to XMLDocument
  vXMLDoc := NewXMLDocument;
  vXMLDoc.ChildNodes.Add(vXML1);
  //And add vXML2 and more...
  vXMLDoc.SaveToFile('C:\Teszt\XmlDocSaveSAmple.XML');
end;

My finally XML what I want to looks like:

<?xml version="1.0"?>
<Sample2>
   <XmlDataSample2>test data</XmlDataSample2>
</Sample2>
<Sample1>
   <XmlDataSample1>test data</XmlDataSample1>
</Sample1>
0

1 Answer 1

2

The XML standard allows only 1 top-level element in a document. You are going to have to make your <Sample... > elements be children of another top-level element, eg:

<?xml version="1.0"?>
<Samples>
  <Sample2>
    <XmlDataSample2>test data</XmlDataSample2>
  </Sample2>
  <Sample1>
    <XmlDataSample1>test data</XmlDataSample1>
  </Sample1>
</Samples>
procedure TForm3.btnXmlSaveToFileClick(Sender: TObject);
var
  vXMLDoc: IXMLDocument;
  vXML1: IXMLDataSample1;
  vXMLSamples: IXMLNode;
begin
  vXML1 := TDataSampleXMLHandler1.NewXML;
  vXML1.XmlDataSample1 := 'test data';

  vXMLDoc := NewXMLDocument;
  vXMLSamples := vXMLDoc.AddChild('Samples');
  vXMLSamples.ChildNodes.Add(vXML1);
  //And add vXML2 and more...
  vXMLDoc.SaveToFile('C:\Teszt\XmlDocSaveSample.XML');
end;
2
  • Thanks a lot Remy Lebeau! Any advice for the perfomance? Addig IXMLNode as a childnode takes about 30 sec...
    – Steve88
    Commented Aug 4, 2019 at 15:14
  • @Steve88 it definitely should not be taking that long to add 1 child node to a parent node. Something is seriously wrong if it is. Now, if you are adding 1000s of child nodes, that would be a different story... Commented Aug 4, 2019 at 19:12

Not the answer you're looking for? Browse other questions tagged or ask your own question.