Search code examples
jsondelphifiredacdelphi-11-alexandria

How to use the DatasetAdapter's JSON export directly, without a RESTRequest?


Delphi offers a DatasetAdapter to export and import datasets to standard JSONs in the form of arrays of objects, without all the quirkiness that FDDataset.SaveToStream(Stream, sfJSON) includes, resulting in shorter and much cleaner files.

This is my quick demo of that export and posterior import of Datasets to simple JSONs:

  // We clear prior JSON export files
  if TFile.Exists('export.json') then
    TFile.Delete('export.json');

  // We create a source Dataset
  var LSource := TFDMemTable.Create(Self);
  LSource.CachedUpdates := True;
  LSource.FieldDefs.Add('id', ftInteger);
  LSource.FieldDefs.Add('description', ftWideString, 100);

  // We populate the source Dataset
  LSource.CreateDataSet;
  LSource.Append;
  LSource.FieldValues['id'] := 1;
  LSource.FieldValues['description'] := 'apples';
  LSource.Post;
  LSource.Append;
  LSource.FieldValues['id'] := 2;
  LSource.FieldValues['description'] := 'oranges';
  LSource.Post;

  // We create the objects to export that Dataset to JSON
  var LRequest := TRESTRequest.Create(Self);
  var LRequestAdapter := TRESTRequestDataSetAdapter.Create(Self);
  LRequestAdapter.Request := LRequest;
  LRequestAdapter.AutoUpdate := false;
  LRequestAdapter.Dataset := LSource;

  // We export the source Dataset to JSON
  LRequestAdapter.UpdateParameter;
  TFile.WriteAllText('export.json', LRequest.GetFullRequestBody);

  // We create a target Dataset
  var LTarget := TFDMemTable.Create(Self);
  LTarget.CachedUpdates := True;
  LTarget.FetchOptions.Items := [];
  LTarget.FieldDefs.Add('id', ftInteger);
  LTarget.FieldDefs.Add('description', ftWideString, 100);

  // We create the objects to import our JSON data to the target Dataset
  var LResponseAdapter := TCustomJSONDatasetAdapter.Create(Self);
  LResponseAdapter.Dataset := LTarget;
  
  // We import the JSON data to the target Dataset
  LResponseAdapter.UpdateDataSet(TJSONValue.ParseJSONValue(TFile.ReadAllText('export.json')));

  // We check that the target contains the same data we populated into the source
  ShowMessage('RowCount: ' + LTarget.RecordCount.ToString + ', First Row: ' + LTarget.FieldByName('Description').AsString);

It works perfectly fine, but I wonder - can I avoid having to use an intermediate TRESTRequest during the export? Most of the time, I will want to put the resulting JSON into a RAD Server's TEndpointResponse, not into TRESTRequest.

The DatasetAdapter unit provides the TCustomJSONDatasetAdapter class to import direct JSONs into Datasets without having to read them from TRESTResponse. But I can't find a similar class in that unit that allows to export Datasets directly to JSON (so I can place that JSON into an EndpointResponse, or further manipulate that JSON, like saving several of them into a single file to reduce round-trips to the backend, etc). I don't like exporting that dataset inside the body of a TRESTRequest that I really won't use, slowing the process and adding unnecessary memory use.


Solution

  • Following Uwe Raabe advice, I used the underlaying TDataSetToJSONBridge class (Data.DBJson unit) instead of the TRESTRequestDataSetAdapter, and now I can get the export JSON directly, without intermediate classes.

    This is the updated demo:

      // We clear prior JSON export files
      if TFile.Exists('export.json') then
        TFile.Delete('export.json');
    
      // We create a source Dataset
      var LSource := TFDMemTable.Create(Self);
      LSource.CachedUpdates := True;
      LSource.FieldDefs.Add('id', ftInteger);
      LSource.FieldDefs.Add('description', ftWideString, 100);
    
      // We populate the source Dataset
      LSource.CreateDataSet;
      LSource.Append;
      LSource.FieldValues['id'] := 1;
      LSource.FieldValues['description'] := 'apples';
      LSource.Post;
      LSource.Append;
      LSource.FieldValues['id'] := 2;
      LSource.FieldValues['description'] := 'oranges';
      LSource.Post;
    
      // We export the source Dataset to JSON
      var ToJSONBridge := TDatasetToJSONBridge.Create;
      ToJSONBridge.Dataset := LSource;
      TFile.WriteAllText('export.json', ToJSONBridge.Produce.ToString);
    
      // We create a target Dataset
      var LTarget := TFDMemTable.Create(Self);
      LTarget.CachedUpdates := True;
      LTarget.FetchOptions.Items := [];
      LTarget.FieldDefs.Add('id', ftInteger);
      LTarget.FieldDefs.Add('description', ftWideString, 100);
    
      // We import the JSON data to the target Dataset
      var LResponseAdapter := TCustomJSONDatasetAdapter.Create(Self);
      LResponseAdapter.Dataset := LTarget;
      LResponseAdapter.UpdateDataSet(TJSONValue.ParseJSONValue(TFile.ReadAllText('export.json')));
    
      // We check that the target contains the same data we populated into the source
      ShowMessage('RowCount: ' + LTarget.RecordCount.ToString + ', First Row: ' + LTarget.FieldByName('Description').AsString);
    

    Much simpler, and it seems to work fine. Thank you Uwe.