Delphi修改现有XML文档结构

时间:2017-03-01 21:09:11

标签: xml delphi xml-parsing tclientdataset

我有一个带嵌套表的现有XML文档。我想打开它,读入并修改结构(即添加或删除列/字段)。忽略嵌套表,这里是一个完整的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>

以下代码在打开时引发异常&#34; testField&#34;没找到,大概是因为它不存在于基础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 doc文件。

<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文档添加字段?

干杯, 唐纳

1 个答案:

答案 0 :(得分:1)

你并没有以正确的方式解决这个问题;对于初学者,CreateDataSet 完全删除以前在ClientDataSet中的任何数据

接下来就是你不想用持久字段和/或来做这件事 FieldDef就位,在您进行更改时清除它们。您之后是否创建它们取决于您,但如果您要在代码中创建TFields,则应从XML元数据中的每个字段创建一个,从CDS中的空字段列表开始。

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

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

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

  • 执行此操作

最后,它通过从更改的XML

加载CDS来关闭并重新打开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中,然后创建如果你愿意,可以使用持久性TField。

XML代码适用于D7,btw附带的MSXML.Pas版本。我倾向于为D7发布代码,除非q要求后来的Delphi版本。