在 Delphi 10.1 中生成一个 XML 文件并执行基本的 XML 操作?

Generate an XML file in Delphi 10.1 and perform basic XML operations?

我想在 Delphi 10.1 中使用以下格式创建一个 XML 文件

<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>

我想从注册表中获取数据,当单击确定按钮时,它应该将来自编辑框、单选按钮等的数据添加到 XML 文件。

我是 Delphi 编程新手,帮我解决这个问题。

我试过这样写代码:

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.

但它返回了如下 XML 文件:

<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>

应用的UI是这样的Image

当我在输入数据后单击“确定”按钮时,它应该通过每次单击“确定”按钮时创建一个新的 <Employee/> 节点将数据写入 XML 文件。

我通常不喜欢提交第二个答案,但你似乎没有跟进 我在第一篇文章中说的。

下面是一个生成新 Xml 文件并添加新员工的示例项目 每次单击 btnAdd 按钮时都会指向它。希望代码中的注释足以让您继续前进。

请注意,此项目使用 MSXML.Pas 单元中定义的 Xml 接口, 它使用Xml文档单元。

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;

更新

您在评论中询问是否可以使用 TXmlDocument 而不是 MSXML.Pas 单元中的接口。我认为那将是一个很好的自我指导 练习所以我不打算包含完整的代码来完成它,但这里有一些提示。

需要进行一些细节更改,例如如何从中加载 XML 文档 Memo1,但主要变化在于如何找到 Employees 节点 XML 文档。执行此操作的最简单方法是使用 XPath 查询(请参阅 SelectSingleNode 在上面的代码中)但涉及使用相同的 XML 接口 和 MSXML.Pas 一样,如果你想这样做,你最好避免使用 TXml文档。如果你想避免使用 XPath,那么你可以写两个函数, 其中 return EmployeeDB 和 Employees 节点。这些可能看起来像这样

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;

您可以在 CreateEmployeeNode 的改编版本中使用这些函数 通过在节点上调用 AddChild('Employee') 添加新的 Employee 节点 return由 GetEmployeesNode 编辑。

顺便说一句,需要写这些函数,其中假设XML文件有一个固定的结构,说明为什么使用XPath是可取的,因为它更容易适应不同的XML结构,例如如果 EmployeeDB 嵌入到一些更大的 Xml 结构中。使用 XPath,这只需要更改 XPath 查询字符串。

必须以这种方式修改代码,以使用 TXMLDocument 创建 XML doc,并在运行时将子项附加到同一文档。谢谢@MartynA 先生的帮助..

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.