Delphi 修改现有 XML 文档结构

Delphi Modify Existing XML Document Structure

我有一个包含嵌套表格的现有 XML 文档。我想打开它,读入并修改结构(即添加或删除 columns/fields)。忽略嵌套表,这是完整的 XML 测试文档:

<DATAPACKET Version="2.0">
  <METADATA>
    <FIELDS>
      <FIELD attrname="StringField" fieldtype="string" WIDTH="20" /> 
      <FIELD attrname="IntField" fieldtype="i4" /> 
    </FIELDS>
    <PARAMS CHANGE_LOG="1 0 4 2 0 4" /> 
  </METADATA>
  <ROWDATA>
    <ROW RowState="4" StringField="String" IntField="234" /> 
    <ROW RowState="4" StringField="234" IntField="24" /> 
  </ROWDATA>
</DATAPACKET>

以下代码在打开时抛出异常 "testField" 未找到,大概是因为它不存在于基础 XML 文件中。

ClientDataSet1.Close;

with TStringField.Create(ClientDataSet1) do 
begin
  FieldName := 'testField';
  DataSet := ClientDataSet1; 
end; 

with ClientDataSet1 do 
begin 
  CreateDataSet; 
  Open; 
end; 

如果我添加:

with ClientDataSet1 do
begin
  FieldDefs.Clear;
  Fields.Clear;
end;

没有抛出异常,但前两个字段消失并且新结构不会写入 XML 文档文件,除非我输入一些数据。

<DATAPACKET Version="2.0">
  <METADATA>
    <FIELDS>
      <FIELD attrname="testField" fieldtype="string" WIDTH="20" /> 
    </FIELDS>
    <PARAMS CHANGE_LOG="1 0 4" /> 
  </METADATA>
  <ROWDATA>
    <ROW RowState="4" testField="12321" /> 
  </ROWDATA>
</DATAPACKET>

是否有标准或推荐的方法可以在不丢失数据的情况下向现有 XML 文档添加字段?

干杯, 坦纳

你的做法不太对;对于初学者,CreateDataSet 完全删除以前在 ClientDataSet

中的任何数据

接下来是您不希望使用持久字段来执行此操作 and/or FieldDef 到位,因此在您进行更改时清除它们。之后是否创建它们取决于您,但如果您要在代码中创建 TFields,则应为 XML 元数据中的 every 字段创建一个,从CDS 中的空字段列表。

下面的示例项目应该向您展示如何获得您想要的东西。它

  • 从 TMemo、Memo1 中的 XML 加载数据集。在我的,我只是复制 并从您的问题中粘贴 XML。这一步基本上是为了表明数据集 已正确填充;

  • 然后,AddFieldToXML 中的代码将新字段添加到 XML 中的元数据中,并且 将结果复制到 Memo2,并将其保存到磁盘。注意:如所写,它不 将任何数据写入新字段,但您应该能够了解如何 从 AddFieldToXML.

  • 开始

最后,它通过从更改后的 XML

加载 CDS 来关闭并重新打开它

代码:

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, DBCtrls, Grids, DBGrids, DB, DBClient, MSXML;

type
  TForm1 = class(TForm)
    CDS1: TClientDataSet;
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    Button1: TButton;
    Memo1: TMemo;
    Memo2: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  public
    ExistingFN : String;
    NewFN : String;
    procedure AddFieldToXML;
    procedure LoadNewData;
  end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
  ExistingFN :=  'C:\Temp\Data.XML';
  NewFN := 'C:\Temp\NewData.XML';

  Memo1.Lines.SaveToFile(ExistingFN);

  CDS1.Fields.Clear;
  CDS1.FieldDefs.Clear;
  CDS1.LoadFromFile(ExistingFN);

end;

procedure TForm1.AddFieldToXML;
var
  XmlDoc: IXMLDOMDocument;
  NodeList : IXmlDOMNodeList;
  Node,
  NewNode : IXmlDomNode;
  E : IXmlDomElement;
  PathQuery : String;
begin
  PathQuery := '/DATAPACKET/METADATA/FIELDS';

  Memo2.Lines.Clear;
  XmlDoc := CoDOMDocument.Create; //CreateOleObject('Microsoft.XMLDOM') as IXMLDOMDocument;
  XmlDoc.Async := False;
  XmlDoc.LoadXML(Memo1.Lines.Text);
  if xmlDoc.parseError.errorCode <> 0 then
    raise Exception.Create('XML Load error:' + xmlDoc.parseError.reason);

  NodeList := XmlDoc.documentElement.SelectNodes(PathQuery);

  if NodeList.length > 0 then begin
    E := XMLDoc.createElement('FIELD');
    NewNode := E as IXMLDomNode;
    E.setAttribute('attrname', 'testField');
    E.setAttribute('fieldtype', 'string');
    E.setAttribute('WIDTH', '20');
    NodeList.item[0].appendChild(NewNode);
  end;
  Memo2.Lines.Text := XMLDoc.documentElement.xml;
  Memo2.Lines.SaveToFile(NewFN);
end;

procedure TForm1.LoadNewData;
begin
  CDS1.Close;
  CDS1.Fields.Clear;
  CDS1.FieldDefs.Clear;
  CDS1.LoadFromFile(NewFN);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  AddFieldToXML;
  LoadNewData;
end;

将新 XML 保存到磁盘后,您可以通过右键单击 CDS 并使用 Load from MyBase file(对于 D7)将其加载到 IDE 中的 CDS ,类似的更高版本),然后根据需要创建持久性 TFields。

XML 代码适用于 D7 附带的 MSXML.Pas 版本,顺便说一句。我倾向于 post D7 的代码,除非 q 需要更高版本的 Delphi 版本。