1

I wanted to create an XML file in Delphi 10.1 using following format

<EmployeeDB>
<Employees>
<Employee e.id="1">
  <eName>value from Edit Box edtName</eName>
<ePlace>value from Edit Box edtPlace</ePlace>
</Employee >
<Employee e.id="2">
  <eName>value from Edit Box edtName</eName>
<ePlace>value from Edit Box edtPlace</ePlace>
</Employee >
<Employee  e.id="3">
  <eName>value from Edit Box edtName</eName>
<ePlace>value from Edit Box edtPlace</ePlace>
</Employee >
</Employees>
</EmployeeDB>

I wanted to take data from a registration form and when the OK button is clicked it should add the data from Edit Boxes, RadioButtons etc.. to XML file.

I am new to Delphi Programming, help me sorting this out.

I tried writing the code in this way:

unit XMLTrail;

interface

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

type
  Tfrm_XMLTrail = class(TForm)
    edt_eID: TEdit;
    edtName: TEdit;
    edtPlace: TEdit;
    Memo1: TMemo;
    btnAdd: TButton;
    procedure btn_AddClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure LoadXML;
    procedure SaveXML;
  public
    { Public declarations }
    XMLDoc1: TXmlDocument;

    iNode, Root1, Root2, Child_Attrib_Name, Child_Attrib_Place: IXmlNode;
    function GetEmployeeDBNode(XMLDoc: TXmlDocument): IXmlNode;
    function GetEmployeesNode(EmployeeDBNode: IXmlNode): IXmlNode;

  end;

var
  frm_XMLTrail: Tfrm_XMLTrail;

implementation

{$R *.dfm}

const
  scXmlTemplate = '<EmployeeDB>'#13#10 + '  <Employees>'#13#10 +
    '  </Employees>'#13#10 + '</EmployeeDB>';

  scXmlFileName =
    'C:\Users\Rajesh\Documents\Embarcadero\Studio\Projects\Samples\XML trail\Win32\Debug\nicexml.xml';

procedure Tfrm_XMLTrail.btn_AddClick(Sender: TObject);
begin
  XMLDoc1.Active := true;
  iNode := XMLDoc1.DocumentElement;
  Root1 := iNode.ChildNodes.FindNode('Employees');
  Root2 := Root1.AddChild('Employee');
  Root2.Attributes['e.id'] := edt_eID.Text;
  Child_Attrib_Name := Root2.AddChild('eName');
  Child_Attrib_Name.Text := edtName.Text;
  Child_Attrib_Place := Root2.AddChild('ePlace');
  Child_Attrib_Place.Text := edtPlace.Text;
  XMLDoc1.Active := true;
  XMLDoc1.SaveToFile(scXmlFileName);

end;

procedure Tfrm_XMLTrail.FormCreate(Sender: TObject);
begin
  XMLDoc1 := TXmlDocument.Create(nil);
  if not FileExists(scXmlFileName) then
  begin
    // XMLDoc1 := TXmlDocument.Create(nil);
    XMLDoc1.Active := true;
    XMLDoc1.Options := [doNodeAutoIndent];
    iNode := XMLDoc1.AddChild('UmangEmployeeDB');
    Root1 := iNode.AddChild('Employees');
  end
  else
  begin
    LoadXML;
  end;
end;

procedure Tfrm_XMLTrail.LoadXML;
begin
  XMLDoc1.LoadFromFile(scXmlFileName);
  Memo1.Lines.Text := XMLDoc1.XML.Text;
  // SaveXML;
  // Memo1.Lines.LoadFromFile(scXmlFileName);

  // XMLDoc1 := TXmlDocument.Create(nil);
  // Assert(Root1 <> Nil);
end;

procedure Tfrm_XMLTrail.SaveXML;
begin
  Memo1.Lines.SaveToFile(scXmlFileName);
end;

end.

But it returned the XML file like below:

<EmployeeDB>
    <Employees>
        <Employee e.id="2">
            <eName>sssss</eName>
            <ePlace>fgr</ePlace>
        </Employee>
        <Employee e.id="2">
            <eName>sssss</eName>
            <ePlace>fgr</ePlace>
        </Employee>
    </Employees>
</EmployeeDB>

The UI of the application is like in this Image.

When I click on OK button after entering the data, it should write the data to XML file by creating a new <Employee/> node every time I click OK button.

10
  • SO is about specific programming problems, not providing "how to" tutorials. What is your question? Commented Sep 28, 2016 at 7:35
  • it is a problem, Iam not able to generate the XML in my desired format. Can u please help me over that, @MartynA Iam new to this Delphi Programming. Commented Sep 28, 2016 at 7:41
  • Even with your edits to your q, you do not clearly state in your question exactly what your "desired" format is, how could anyone possibly help you achieve it? Commented Sep 28, 2016 at 7:59
  • I wanted to create XML file in the format which is on top of Post. <EmployeeDB> <Employees> <Employee e.id="1"> <eName>value from Edit Box edtName</eName> <ePlace>value from Edit Box edtPlace</ePlace> </Employee > <Employee e.id="2"> <eName>value from Edit Box edtName</eName> <ePlace>value from Edit Box edtPlace</ePlace> </Employee > <Employee e.id="3"> <eName>value from Edit Box edtName</eName> <ePlace>value from Edit Box edtPlace</ePlace> </Employee > </Employees> </EmployeeDB> Commented Sep 28, 2016 at 8:01
  • 1
    Please. Do. Not. Ask. In. Comments. Commented Sep 28, 2016 at 8:38

2 Answers 2

2

I normally don't like submitting a second answer but you didn't seem to follow what I said in my first one.

Below is a sample project which generates a new Xml file and adds a new Employee node to it every time you click the btnAdd button. The comments in the code should hopefully be sufficient to get you going.

Note that this project uses the Xml interfaces defined in the MSXML.Pas unit, it does not use the XmlDoc unit.

unit XmlGenerateu;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, MSXML;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    edtName: TEdit;
    edtPlace: TEdit;
    btnAdd: TButton;
    procedure btnAddClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    procedure LoadXML;
    procedure SaveXML;
    procedure GenerateXML;
    function CreateEmployeeNode: IXmlDomElement;
  protected
  public
    XMLDoc : IXmlDomDocument;
    EmployeesNode : IXmlDomNode;
    NewEmployeeNode : IXmlDomNode;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

const
  scXmlTemplate =
    '<EmployeeDB>'#13#10
     + '  <Employees>'#13#10
     + '  </Employees>'#13#10
     + '</EmployeeDB>';

  scXmlFileName = 'C:\Temp\Employees.Xml';

procedure TForm1.btnAddClick(Sender: TObject);
begin
  NewEmployeeNode := CreateEmployeeNode;
  Memo1.Lines.Text := XmlDoc.xml;
  SaveXML;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  LoadXML;
end;

procedure TForm1.LoadXML;
begin
  //  If there is no existing Xml file create it from our Xml template
  //  and save it to disk
  if not FileExists(scXmlFileName) then begin
    Memo1.Lines.Text := scXmlTemplate;
    SaveXML;
  end;
  Memo1.Lines.LoadFromFile(scXmlFileName);

  //  Create the XmlDoc object
  XmlDoc := CoDomDocument.Create;
  //  Load it from Memo1
  Assert(XmlDoc.LoadXml(Memo1.Lines.Text));

  //  Find the Employees node
  EmployeesNode := XmlDoc.selectSingleNode('/EmployeeDB/Employees');
  //  Complain if we didn't find it
  Assert(EmployeesNode <> Nil);

end;

procedure TForm1.SaveXML;
begin
  Memo1.Lines.SaveToFile(scXmlFileName);
end;

function TForm1.CreateEmployeeNode : IXmlDomElement;
var
  EmployeeID : Integer;
  NameElement,
  PlaceElement : IXmlDomElement;
begin
  //  First, generate the ID for the new Employee
  EmployeeID :=  EmployeesNode.childNodes.length + 1;

  //  Next, get the XmlDoc to create a new Element
  Result := XmlDoc.createElement('Employee');

  //  Set the new Element's ID attribute
  Result.setAttribute('e.id', IntToStr(EmployeeID));

  //  Put the new Employee node at the end of the list of Employee nodes
  EmployeesNode.appendChild(Result);

  //  Finally, createt eName and ePlace children of the new Employee
  NameElement := XmlDoc.createElement('eName');
  NameElement.text := edtName.Text;
  Result.appendChild(NameElement);

  PlaceElement := XmlDoc.createElement('ePlace');
  PlaceElement.text := edtPlace.Text;
  Result.appendChild(PlaceElement);
end;

Update

You asked in a comment whether this could be done using TXmlDocument instead of the interfaces in the MSXML.Pas unit. I think that would be a good self-instructional exercise so I am not going to include the complete code to do it, but here are some hints.

There are some detail changes necessary, e.g. how to load the XMLDocument from Memo1, but the main change is in how to find the Employees nodes of the XML document. The simplest way to do this is to use an XPath query (see SelectSingleNode in the code above) but that involves using the same XML interfaces as are in MSXML.Pas and if you want to do that, you might as well avoid using TXmlDocument. If you want to avoid using XPath, then you can write two functions, which return the EmployeeDB and Employees nodes. These might look like this

function TForm1.GetEmployeeDBNode(XmlDoc : TXmlDocument) : IXmlNode;
begin
  Result := XmlDoc.DocumentElement;
  Assert(Result <> Nil);
  Assert(Result.NodeName = 'EmployeeDB');
end;

function TForm1.GetEmployeesNode(EmployeeDBNode : IXmlNode) : IXmlNode;
begin
  Result := EmployeeDBNode.ChildNodes.First;
  Assert(Result <> Nil);
  Assert(Result.NodeName = 'Employees');
end;

You could use these functions in an adapted version of CreateEmployeeNode to add the new Employee node by calling AddChild('Employee') on the node returned by GetEmployeesNode.

Btw, the need to write these functions, which assume XML document has a fixed structure, illustrate why using XPath is preferable, because it is easier to accommodate different XML structures, for example if the EmployeeDB were embedded in some larger Xml structure. Using XPath, this would require merely changing the XPath query string.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the answer, It is working fine,u did it using IXmlDomDocument and IXmlDomNode, can we do this same using TXMLDocument and IXMLNode.
Of course, but you should learn how to do this yourself. It should require only minor adaption and restructing of the code in your q.
Hello @MartynA, I Modified the Code as u said using TXMLDocument, Iam able to create the document, But i try to Load the existing doc and add a new Node <Employee/> . It is not saving , The code in Addbtn Click is executing and when comming to these statements it showing Abstract error XMLDoc1.Active := true; XMLDoc1.SaveToFile(scXmlFileName);
@MohanRajesh: I am sorry but I cannot debug your version for you. I re-implemented my previous code for TXmlDocument in a few minutes and it works fine, no abstract error and it saves correctly. You must have made a mistake somewhere. If you cannot find it, I suggest you post your code in a new question. It is not suitable to deal with here.
I got the error and Solved it, the problem is , I havenot given the owner for TXMLDocument so that it is getting inactive and getting destructed in the middle , i given the Owner for TXMLDocument and now it is working fine.. Thanks for the help..
1

The Code has to be modified this way , to create XML doc using TXMLDocument and append childs to same document while runtime. Thank You Mr @MartynA for help..

unit XMLTrail;

interface

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

type
  Tfrm_XMLTrail = class(TForm)
    edt_eID: TEdit;
    edtName: TEdit;
    edtPlace: TEdit;
    Memo1: TMemo;
    btnAdd: TButton;
    procedure btn_AddClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure LoadXML;
    procedure SaveXML;
  public
    { Public declarations }
    gXMLDoc1: TXmlDocument;
    giNode, gRoot1, gRoot2, gChild_Attrib_Name, gChild_Attrib_Place: IXmlNode;
    function GetEmployeeDBNode(XMLDoc: TXmlDocument): IXmlNode;
    function GetEmployeesNode(EmployeeDBNode: IXmlNode): IXmlNode;
    function GetEmployeeNodes(EmployeeNodes: IXmlNode): IXmlNode;
  end;

var
  frm_XMLTrail: Tfrm_XMLTrail;

implementation

{$R *.dfm}

const
  scXmlTemplate = '<EmployeeDB>'#13#10 + '  <Employees>'#13#10 +
    '  </Employees>'#13#10 + '</EmployeeDB>';

  scXmlFileName =
    '..\nicexml.xml';

procedure Tfrm_XMLTrail.btn_AddClick(Sender: TObject);
begin
  gXMLDoc1.Active := true;
  giNode := gXMLDoc1.DocumentElement;
  gRoot1 := giNode.ChildNodes.FindNode('Employees');
  gRoot2 := gRoot1.AddChild('Employee');
  gRoot2.Attributes['e.id'] := edt_eID.Text;
  gChild_Attrib_Name := gRoot2.AddChild('eName');
  gChild_Attrib_Name.Text := edtName.Text;
  gChild_Attrib_Place := gRoot2.AddChild('ePlace');
  gChild_Attrib_Place.Text := edtPlace.Text;
  gXMLDoc1.Active := true;
  gXMLDoc1.XML.Text := XMLDoc.FormatXMLData(gXMLDoc1.XML.Text);
  Memo1.Lines.Text := gXMLDoc1.XML.Text;
  SaveXML;
end;

procedure Tfrm_XMLTrail.FormCreate(Sender: TObject);
begin
  gXMLDoc1 := TXmlDocument.Create(self);
  if not FileExists(scXmlFileName) then
  begin
    // XMLDoc1 := TXmlDocument.Create(nil);
    gXMLDoc1.Active := true;
    gXMLDoc1.Options := [doNodeAutoIndent];
    giNode := gXMLDoc1.AddChild('EmployeeDB');
    gRoot1 := giNode.AddChild('Employees');
  end
  else
  begin
    LoadXML;
  end;
end;

procedure Tfrm_XMLTrail.LoadXML;
begin
  gXMLDoc1.LoadFromFile(scXmlFileName);
  gXMLDoc1.Active := true;
  Memo1.Lines.Text := gXMLDoc1.XML.Text;
end;

procedure Tfrm_XMLTrail.SaveXML;
begin
  Memo1.Lines.SaveToFile(scXmlFileName);
end;

function Tfrm_XMLTrail.GetEmployeeDBNode(XMLDoc: TXmlDocument): IXmlNode;
begin
  Result := XMLDoc.DocumentElement;
  Assert(Result <> Nil);
  Assert(Result.NodeName = 'EmployeeDB');
end;

function Tfrm_XMLTrail.GetEmployeesNode(EmployeeDBNode: IXmlNode): IXmlNode;
begin
  Result := EmployeeDBNode.ChildNodes.First;
  Assert(Result <> Nil);
  Assert(Result.NodeName = 'Employees');
end;

function Tfrm_XMLTrail.GetEmployeeNodes(EmployeeNodes: IXmlNode): IXmlNode;
begin
  Result := EmployeeNodes.ChildNodes.First;
  Assert(Result <> Nil);
  Assert(Result.NodeName = 'Employee');
end;

end.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.