简体   繁体   中英

How do I get a dynamic TClientDataset (based on TRESTResponseDataSetAdapter) to correctly recognise boolean values?

I've built a delphi client sample in Delphi XE5 that utilises the Rest library:

  • TRestClient
  • TRestRequest
  • TRestResponse
  • TRESTResponseDataSetAdapter
  • TClientDataSet (and TDataSource bound to TDBGrid and
    TcxGridDBTableView)

All served by an ASP.net Web API that generates a dataset based on dynamic queries from Delphi. I can successfully run the queries and return data to the client and render the data, however boolean fields are rendered blank.

  • How do I get TClientDataSet to recognise the data types correctly?
  • Is this the proper way to use the TRESTResponseDataSetAdapter or TClientDataSet?
  • Does anyone have any experience with these components

EDIT Surprisingly, Embarcadero's TRestResponseDatasetAdapter does this internally to create fields:

procedure TCustomJSONDataSetAdapter.CB_CollectFieldDefs(const AJSONObject: TJSONObject);
var
  LJSONPair: TJSONPair;
begin
  for LJSONPair in AJSONObject do
  begin
    DoAddDataSetFieldDef(LJSONPair.JsonString.Value, ftString);
  end;
end;

The field types are all hardcoded to ftString!

This is the returned json:

"Table": [
        {
            "Id": 34,
            "Node": "Navision_ASN_1",
            "FormatId": 2,
            "Value": null,
            "ParentID": null,
            "DocumentOrder": 1,
            "Combined": false,
            "Delimiter": "",
            "ValueType": 0,
            "Ignore": false,
            "IsIndexer": false,
            "IsCounter": false,
            "StringFormat": null
        },
        {
            "Id": 35,
            "Node": "MessageHeader",
            "FormatId": 2,
            "Value": null,
            "ParentID": 34,
            "DocumentOrder": 2,
            "Combined": false,
            "Delimiter": "",
            "ValueType": 0,
            "Ignore": false,
            "IsIndexer": false,
            "IsCounter": false,
            "StringFormat": null
        },
        {
            "Id": 52,
            "Node": "Consignment",
            "FormatId": 2,
            "Value": null,
            "ParentID": 34,
            "DocumentOrder": 13,
            "Combined": false,
            "Delimiter": "",
            "ValueType": 0,
            "Ignore": false,
            "IsIndexer": false,
            "IsCounter": false,
            "StringFormat": null
        },
        {
            "Id": 53,
            "Node": "Line",
            "FormatId": 2,
            "Value": null,
            "ParentID": 52,
            "DocumentOrder": 18,
            "Combined": false,
            "Delimiter": "",
            "ValueType": 0,
            "Ignore": false,
            "IsIndexer": false,
            "IsCounter": false,
            "StringFormat": null
        }
    ]

You can't!

The issue comes down to the TJsonObject class from Embarcadero Delphi. In the Rest.Response.Adapter unit the json returned is parsed into a TJsonObject, and then all the TJsonPairs in the object are iterated over in CB_CollectFieldDefs and CB_CollectFieldData.

First problem: CB_CollectionFieldDefs hardcodes ftString as the data type to add to the dataSet for each field definition. So I returned the field types from my Web API call to remove the guesswork and built the field definitions manually. When the RestResponseDataSetAdapter has field definitions, the unit correctly skips CB_CollectionFieldDefs, this leads to the second problem.

Second problem : the TjsonPair in the object does not correctly parse boolean json values. By the time CB_CollectFieldData is called, all the boolean values which should be variant types are empty strings. So we get the exception 'Could not convert variant of type (UnicodeString) into type (Boolean)'.

I have sadly had to drop this promising set of components in favour of enhancing Fabricio Colombo's excellent rest client api. Using the pattern in his GetAsDataSet logic, I created a number of post methods to post json (since the json creation from built in delphi units were incorrect, I substituted with SuperObject). I introduced PostJson and CreateDataset which both return TClientDataSets.

function TResource.CreateDataset(data: string; table: string = ''; titles: string = ''): TClientDataSet;
var
  vJson: ISuperObject;
begin
  vJson := SuperObject.SO(data);

  Result := TJsonToDataSetConverter.CreateDataSetMetadata(vJson, table, titles);
  TJsonToDataSetConverter.ToDataSet(Result, vJson.O[table]);
end;
// which allows me to do:
ds := jsonrestclient1.Resource(url)
          .ContentType(RestUtils.MediaType_Json)
          .Accept(restutils.MediaType_Json)
          .PostJson(json, 'Table', 'Titles');
// or
rs := jsonrestclient1.Resource(url)
          .ContentType(RestUtils.MediaType_Json)
          .Accept(restutils.MediaType_Json);
data:= rs.PostJson(Query) ;
vJson := SO(data);
fHasMore := vJson.B['HasMore'];
ds:= rs.CreateDataset(data, 'Table', 'Titles');

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM