Bill
Bill

Reputation:

Restructure a TClientdataset

Can a TClientDataSet XML file be restructured without losing data? Are there any demo applications or source code that shows how to do such a restructure?

Upvotes: 3

Views: 700

Answers (2)

Phil B
Phil B

Reputation: 435

In order to make changes to CDS structure on disk I used a sub-class outlined below. We write our data in binary format to a stream (before compression/encryption) but it should work much the same for XML format.

If you need to add/remove any fields from your saved dataset or change the field definitions then you just increment the dataset table version. When the dataset is opened each time it compares the saved version number with the current. If the saved table is old it will be copied into the new structure, so if you need to make changes, you will take one performance hit the first time you reload the table, but after that it should load from disk as usual.

So, if you save the CDS back to disk after doing the merge - voila - your XML structure is updated, in a CDS friendly format.

TCDS = class(TCustomClientDataset)
private
 fTableVersion: integer;
 /// <summary> Copies records from source with potentially different table
 ///  structure/field defs from self, providing defaults for missing fields</summary>
 procedure CopyFromDataset(const ASource: TCustomClientDataset);
 /// <summary>Provide a default value, if necessary, for any new fields</summary>
 function GetDefaultValue(const AFieldName: string): variant;
public
 procedure LoadFromStream(AStream: TStream);
 procedure SaveToStream(AStream: TStream);
end;

procedure TCDS.LoadFromStream(AStream: TStream);
var
 ATemp: TCDS;
 APersistedVersion: integer;
begin
 AStream.ReadData(APersistedVersion);
 if APersistedVersion = fTableVersion then
 begin
  Close;
  ReadDataPacket(AStream, True);
  Open;
 end
 else if APersistedVersion < fTableVersion then
 begin
  // It's an old table structure:
  // - Load old structure into temp CDS
  // - Merge temp CDS records into new structure
  ATemp := TCDS.Create;
  try
   ATemp.Close;
   ATemp.ReadDataPacket(AStream, True);
   ATemp.Open;
   CopyFromDataset(ATemp);
  finally
   FreeAndNil(ATemp);
  end;
 end;
end;

procedure TCDS.SaveToStream(AStream: TStream);
begin
 AStream.WriteData(fVersionNumber);
 WriteDataPacket(AStream, True);
end;

procedure TCDS.CopyFromDataset(const ASource: TCustomClientDataset);
var
 ACurrentFieldNames: TStrings;
 i: integer;
begin
 // Assuming we don't want to keep any records already in dataset
 EmptyDataSet;
 ACurrentFieldNames := TStringList.Create;
 try
  Fields.GetFieldNames(ACurrentFieldNames);
  for i := 0 to ACurrentFieldNames.Count-1 do
   ACurrentFieldNames.Objects[i] := ASource.Fields.FindField(ACurrentFieldNames[i]);

  ASource.First;
  while not ASource.Eof do
  begin
   Append;
   for i := 0 to Fields.Count-1 do
   begin
    if Assigned(ACurrentFieldNames.Objects[i]) then
     Fields[i].Value := TField(ACurrentFieldNames.Objects[i]).Value
    else if Fields[i].Required then
     Fields[i].Value := GetDefaultValue(ACurrentFieldNames[i]);
    end;
    Post;
    ASource.Next;
   end;
 finally
  FreeAndNil(ACurrentFieldNames);
 end;
end; 

Upvotes: 0

Tim Jarvis
Tim Jarvis

Reputation: 18825

yes and no, the xml doc is transformed using XLST so it needs to conform to that template to be able to be read by the TClientDataSet.

However that does also mean that you can also transform the doc to any format you like yourself into a separate doc, you just can't load the transformed doc directly into the TClientDataSet.

EDIT: Oops, forgot to post an example.

This project on code central shows a transform from clientdataset to an ADO recordset.

Upvotes: 1

Related Questions