TWebCore and XDATA

I tried it and I read the documentation, but I am not able to solve it, sorry!


When I check the Response.ResponseText I can see, that the data are in.

When I try "XDS_Abteilung.SetJsonData(TJSArray(Response.Result));" nothing happens. I wanted to check an fieldvalue by:

XDS_Abteilung.Active:=true;
XDS_Abteilung.First;
Showmessage(XDS_Abteilung.FieldByName('abteilung').AsString);

But I get the error message: "EntitySetName not specified"

When I try fill the DataSet by 
"XDS_Abteilung.SetJsonData(TJSArray(TJSObject(Response.Result)['value']));"
I get the error message: Can not read property 'value' of null as OnResponse

Can you support me?

For a better understanding the whole procedure with the service all and the OnResponse procedure...


procedure TFrmMain.btnAbteilungClick(Sender: TObject);
   procedure OnResponse(Response: TXDataClientResponse);
   begin
      showmessage(Response.ResponseText);
      XDS_Abteilung.SetJsonData(TJSArray(Response.Result));
      XDS_Abteilung.Active:=true;
      XDS_Abteilung.First;
      Showmessage(XDS_Abteilung.FieldByName('abteilung').AsString);
   end;
begin
   XMyClient.RawInvoke('IData_xChangeService.Get_Abteilung',[], @OnResponse);
end;

The content of Response.Result:

{"FDBS":{"Version":15,"Manager":{"UpdatesRegistry":true,"TableList":[{"class":"Table","Name":"Query_Main","SourceName":"abteilung","SourceID":1,"TabID":0,"EnforceConstraints":false,"MinimumCapacity":50,"ColumnList":[{"class":"Column","Name":"AbteilungsID","SourceName":"AbteilungsID","SourceID":1,"DataType":"UInt32","Precision":10,"Searchable":true,"AllowNull":true,"AutoInc":true,"Base":true,"AutoIncrementSeed":-1,"AutoIncrementStep":-1,"OAllowNull":true,"OInWhere":true,"OInKey":true,"OAfterInsChanged":true,"OriginTabName":"nedcom2.abteilung","OriginColName":"AbteilungsID","SourcePrecision":10},{"class":"Column","Name":"Abteilung","SourceName":"Abteilung","SourceID":2,"DataType":"AnsiString","Size":45,"Searchable":true,"Base":true,"OInUpdate":true,"OInWhere":true,"OriginTabName":"nedcom2.abteilung","OriginColName":"Abteilung","SourceSize":45},{"class":"Column","Name":"Test","SourceName":"Test","SourceID":3,"DataType":"AnsiString","Size":45,"Searchable":true,"Default":true,"Base":true,"OAllowNull":true,"OInUpdate":true,"OInWhere":true,"OAfterInsChanged":true,"OriginTabName":"nedcom2.abteilung","OriginColName":"Test","SourceSize":45}],"ConstraintList":[],"ViewList":[],"RowList":[{"RowID":0,"Original":{"AbteilungsID":10,"Abteilung":"Verkauf","Test":"0"}},{"RowID":1,"Original":{"AbteilungsID":20,"Abteilung":"Anwendung & Entwicklung","Test":"0"}},{"RowID":2,"Original":{"AbteilungsID":30,"Abteilung":"Technische B?ro","Test":"0"}},{"RowID":3,"Original":{"AbteilungsID":40,"Abteilung":"Arbeitsvorbereitung","Test":"0"}},{"RowID":4,"Original":{"AbteilungsID":50,"Abteilung":"Produktion","Test":"0"}},{"RowID":5,"Original":{"AbteilungsID":60,"Abteilung":"Werkzeugbau","Test":"0"}},{"RowID":6,"Original":{"AbteilungsID":70,"Abteilung":"Einkauf","Test":"0"}},{"RowID":7,"Original":{"AbteilungsID":80,"Abteilung":"Qualit?tssicherung","Test":"0"}},{"RowID":8,"Original":{"AbteilungsID":90,"Abteilung":"EDV","Test":"0"}},{"RowID":9,"Original":{"AbteilungsID":100,"Abteilung":"Finanzbuchhaltung","Test":"0"}},{"RowID":10,"Original":{"AbteilungsID":110,"Abteilung":"Gesch?ftsf?hrung","Test":"0"}},{"RowID":11,"Original":{"AbteilungsID":111,"Abteilung":"PM","Test":"0"}}]}],"RelationList":[]}}}

Do I have to define the fields? I tried it manually without a EntitySetValue. Afterwards I got a new error-message:


ERROR
Uncaught TypeError: Cannot read property 'FAttributes' of null | TypeError: Cannot read property 'FAttributes' of null at Object.LoadData (http://localhost:8000/Web_App/Web_App.js:45609:113) at Object.ActiveChange (http://localhost:8000/Web_App/Web_App.js:45766:14) at Object.cb [as FOnActiveChange] (http://localhost:8000/Web_App/Web_App.js:222:26) at Object.ActiveChanged (http://localhost:8000/Web_App/Web_App.js:43368:46) at Object.CheckActiveAndEditing (http://localhost:8000/Web_App/Web_App.js:33702:14) at Object.DataEvent (http://localhost:8000/Web_App/Web_App.js:33766:14) at Object.DistributeEvent (http://localhost:8000/Web_App/Web_App.js:34021:44) at Object.ProcessEvent (http://localhost:8000/Web_App/Web_App.js:34045:12) at Object.DataEvent (http://localhost:8000/Web_App/Web_App.js:32534:52) at Object.SetState (http://localhost:8000/Web_App/Web_App.js:32989:14)
at http://localhost:8000/Web_App/Web_App.js [45609:113]
I tried it on this way:
XDS_Abteilung.Close;
XDS_Abteilung.SetJsonData(TJSArray(TJSObject(Response.Result)['FDBS.Manager.TableList.RowList']));
XDS_Abteilung.Open;

But I get the error-message:  Cannot read property 'FDBS.Manager.TableList.RowList' of null at OnResponse

That is the formated JSON-Stream:
{"FDBS":
{"Version":15,"Manager":
{"UpdatesRegistry":true,"TableList":[
{"class":"Table","Name":"Query_Main","SourceName":"abteilung","SourceID":1,"TabID":0,"EnforceConstraints":false,"MinimumCapacity":50,"ColumnList":[
{"class":"Column","Name":"AbteilungsID","SourceName":"AbteilungsID","SourceID":1,"DataType":"UInt32","Precision":10,"Searchable":true,"AllowNull":true,"AutoInc":true,"Base":true,"AutoIncrementSeed":-1,"AutoIncrementStep":-1,"OAllowNull":true,"OInWhere":true,"OInKey":true,"OAfterInsChanged":true,"OriginTabName":"nedcom2.abteilung","OriginColName":"AbteilungsID","SourcePrecision":10},
{"class":"Column","Name":"Abteilung","SourceName":"Abteilung","SourceID":2,"DataType":"AnsiString","Size":45,"Searchable":true,"AllowNull":true,"Default":true,"Base":true,"OAllowNull":true,"OInUpdate":true,"OInWhere":true,"OAfterInsChanged":true,"OriginTabName":"nedcom2.abteilung","OriginColName":"Abteilung","SourceSize":45},
{"class":"Column","Name":"Test","SourceName":"Test","SourceID":3,"DataType":"AnsiString","Size":45,"Searchable":true,"AllowNull":true,"Default":true,"Base":true,"OAllowNull":true,"OInUpdate":true,"OInWhere":true,"OAfterInsChanged":true,"OriginTabName":"nedcom2.abteilung","OriginColName":"Test","SourceSize":45}],
"ConstraintList":[],"ViewList":[],"RowList":[
{"RowID":0,"Original":
{"AbteilungsID":10,"Abteilung":"Verkauf","Test":"0"}
},
{"RowID":1,"Original":
{"AbteilungsID":20,
"Abteilung":"Anwendung & Entwicklung",
"Test":"0"}
},
{"RowID":2,"Original":
{"AbteilungsID":30,
"Abteilung":"Technische Büro",
"Test":"0"}
},
{"RowID":3,"Original":
{"AbteilungsID":40,
"Abteilung":"Arbeitsvorbereitung",
"Test":"0"}
},
{"RowID":4,"Original":
{"AbteilungsID":50,
"Abteilung":"Produktion",
"Test":"0"}
}]
}],
"RelationList":[]}
}
}

Your JSON contain several structures, this is how you should access the array (you can simplify this code of course using a single line, I have created several variables for better understanding:


var

  JFDBS: TJSObject;

  JManager: TJSObject;  

  JTableList: TJSObject;

  JRowList: TJSArray;


..

  JFDBS := TJSObject(TJSObject(Response.Result)['FDBS']);

  JManager := TJSObject(JFDBS['Manager']);

  JTableList := TJSObject(JManager['TableList']);

  JRowList := TJSArray(JTableLlist['RowList']);


  XDS_Abteilung.Close;

  XDS_Abteilung.SetJsonData(JRowList);

  XDS_Abteilung.Open;

And you have to define fields, yes.  
  

Hi Wagner,


thanks for support! I defined the fields and changed my procedure like you descripted:

procedure TFrmMain.btnAbteilungClick(Sender: TObject);
   procedure OnResponse(Response: TXDataClientResponse);
   var JFDBS: TJSObject;
         JManager: TJSObject;  
         JTableList: TJSObject;
         JRowList: TJSArray;
   begin
      JFDBS := TJSObject(TJSObject(Response.Result)['FDBS']);
      JManager := TJSObject(JFDBS['Manager']);
      JTableList := TJSObject(JManager['TableList']);
      JRowList := TJSArray(JTableList['RowList']);
      XDS_Abteilung.Close;
      XDS_Abteilung.SetJsonData(JRowList);
      XDS_Abteilung.Open;
   end;
begin
   XMyClient.RawInvoke('IData_xChangeService.Get_Abteilung',[], @OnResponse);
end;


But unfortunally I get an error-message, like before:
ERROR
Uncaught TypeError: Cannot read property 'FDBS' of null | TypeError: Cannot read property 'FDBS' of null at OnResponse (http://localhost:8000/Web_App/Web_App.js:57008:62) at Object.DoLoad (http://localhost:8000/Web_App/Web_App.js:50388:11) at Object.DoLoad (http://localhost:8000/Web_App/Web_App.js:50662:20) at OnSuccess (http://localhost:8000/Web_App/Web_App.js:50778:19) at LocalSuccess (http://localhost:8000/Web_App/Web_App.js:50033:9) at XMLHttpRequest.XhrLoad (http://localhost:8000/Web_App/Web_App.js:50132:34)
at http://localhost:8000/Web_App/Web_App.js [57008:62]

What can be caused the error?

Greetings from Germany
Patrick

I checked the function via swagger. May be the following message helps to understand what is happen:

"Unrecognized response type; displaying content as text."

The function is defined:
function TData_xChangeService.Get_Abteilung: TStream;
var   LResult: TMemoryStream;
      SQL_String: string;
begin
   LResult:=TMemoryStream.Create;
   SQL_String:='select * from abteilung';
   DBController.Get_Data( LResult, SQL_String );
   Result:= LResult;
end;

The function Get_Data:
procedure TDBController.Get_Data (AStream: TStream; SQL_String: String);
begin
   if Assigned(AStream) then begin
      Query_Main.SQL.Text:=SQL_String;
      Query_Main.Open;
      Query_Main.SaveToStream(AStream, sfJSON);
   end;
end;

I expected that the result is JSON-format?

Patrick


First you need to make sure your server is returning correct data. Open your browser console and check the network tab to see what the server is returning when you are invoking the IData_xChangeService.Get_Abteilung method. This way we can focus either on client or server.

when I call the funtion (http://localhost:8000/tms/xdata/Data_xChangeService/Get_Abteilung) in the browser, taht is the result:
// 20191105151638
// http://localhost:8000/tms/xdata/Data_xChangeService/Get_Abteilung

{
  "FDBS": {
    "Version": 15,
    "Manager": {
      "UpdatesRegistry": true,
      "TableList": [
        {
          "class": "Table",
          "Name": "Query_Main",
          "SourceName": "abteilung",
          "SourceID": 1,
          "TabID": 0,
          "EnforceConstraints": false,
          "MinimumCapacity": 50,
          "ColumnList": [
            {
              "class": "Column",
              "Name": "AbteilungsID",
              "SourceName": "AbteilungsID",
              "SourceID": 1,
              "DataType": "UInt32",
              "Precision": 10,
              "Searchable": true,
              "AllowNull": true,
              "AutoInc": true,
              "Base": true,
              "AutoIncrementSeed": -1,
              "AutoIncrementStep": -1,
              "OAllowNull": true,
              "OInWhere": true,
              "OInKey": true,
              "OAfterInsChanged": true,
              "OriginTabName": "nedcom2.abteilung",
              "OriginColName": "AbteilungsID",
              "SourcePrecision": 10
            },
            {
              "class": "Column",
              "Name": "Abteilung",
              "SourceName": "Abteilung",
              "SourceID": 2,
              "DataType": "AnsiString",
              "Size": 45,
              "Searchable": true,
              "AllowNull": true,
              "Default": true,
              "Base": true,
              "OAllowNull": true,
              "OInUpdate": true,
              "OInWhere": true,
              "OAfterInsChanged": true,
              "OriginTabName": "nedcom2.abteilung",
              "OriginColName": "Abteilung",
              "SourceSize": 45
            },
            {
              "class": "Column",
              "Name": "Test",
              "SourceName": "Test",
              "SourceID": 3,
              "DataType": "AnsiString",
              "Size": 45,
              "Searchable": true,
              "AllowNull": true,
              "Default": true,
              "Base": true,
              "OAllowNull": true,
              "OInUpdate": true,
              "OInWhere": true,
              "OAfterInsChanged": true,
              "OriginTabName": "nedcom2.abteilung",
              "OriginColName": "Test",
              "SourceSize": 45
            }
          ],
          "ConstraintList": [
            
          ],
          "ViewList": [
            
          ],
          "RowList": [
            {
              "RowID": 0,
              "Original": {
                "AbteilungsID": 10,
                "Abteilung": "Verkauf",
                "Test": "0"
              }
            },
            {
              "RowID": 1,
              "Original": {
                "AbteilungsID": 20,
                "Abteilung": "Anwendung & Entwicklung",
                "Test": "0"
              }
            },
            {
              "RowID": 2,
              "Original": {
                "AbteilungsID": 30,
                "Abteilung": "Technische Büro",
                "Test": "0"
              }
            },
            {
              "RowID": 3,
              "Original": {
                "AbteilungsID": 40,
                "Abteilung": "Arbeitsvorbereitung",
                "Test": "0"
              }
            },
            {
              "RowID": 4,
              "Original": {
                "AbteilungsID": 50,
                "Abteilung": "Produktion",
                "Test": "0"
              }
            },
            {
              "RowID": 5,
              "Original": {
                "AbteilungsID": 60,
                "Abteilung": "Werkzeugbau",
                "Test": "0"
              }
            },
            {
              "RowID": 6,
              "Original": {
                "AbteilungsID": 70,
                "Abteilung": "Einkauf",
                "Test": "0"
              }
            },
            {
              "RowID": 7,
              "Original": {
                "AbteilungsID": 80,
                "Abteilung": "Qualitätssicherung",
                "Test": "0"
              }
            },
            {
              "RowID": 8,
              "Original": {
                "AbteilungsID": 90,
                "Abteilung": "EDV",
                "Test": "0"
              }
            },
            {
              "RowID": 9,
              "Original": {
                "AbteilungsID": 100,
                "Abteilung": "Finanzbuchhaltung",
                "Test": "0"
              }
            },
            {
              "RowID": 10,
              "Original": {
                "AbteilungsID": 110,
                "Abteilung": "Geschäftsführung",
                "Test": "0"
              }
            },
            {
              "RowID": 11,
              "Original": {
                "AbteilungsID": 111,
                "Abteilung": "PM",
                "Test": "0"
              }
            }
          ]
        }
      ],
      "RelationList": [
        
      ]
    }
  }
}
Queued at 0
Started at 0.96 ms
Resource Scheduling DURATION
Queueing
0.96 ms
Connection Start DURATION
Stalled
0.57 ms
Request/Response DURATION
Request sent
69 μs
Waiting (TTFB)
1.91 ms
Content Download
0.47 ms
Explanation 3.97 ms

Is that helpful for you?

1. The server response is configured as a TStream. Thus, the result will always come, in web core, as a string. This is what you need to correctly parse the result:


      JFDBS := TJSObject(TJSObject(TJSJson.Parse(string(Response.Result)))['FDBS']);
      JManager := TJSObject(JFDBS['Manager']);
      JTableList := TJSObject(TJSArray(JManager['TableList'])[0]);
      JRowList := TJSArray(JTableList['RowList']);
      console.log(JRowList);


2. In your JSON result, TableList is an array, not an object. Thus you need to get the first item of an array (I’m not sure what you should do if you have two items in the array, but that is specific to your application):


      JFDBS := TJSObject(TJSObject(TJSJson.Parse(string(Response.Result)))['FDBS']);
      JManager := TJSObject(JFDBS['Manager']);
      JTableList := TJSObject(TJSArray(JManager['TableList'])[0]);
      JRowList := TJSArray(JTableList['RowList']);
      console.log(JRowList);


3. Since in your JSON result the actual data come in a subject (Original):


                "RowList": [{
                    "RowID": 0,
                    "Original": {
                        "AbteilungsID": 10,
                        "Abteilung": "Verkauf",
                        "Test": "0"
                    }
                }, 


You need to create your dataset fields with field name prefixed with "Original.", e.g.:

Original.AbteilungsID
Original.Abteilung
Original.Test

I implemented all procedures you discuss and my fiinal code on client application is below. But i have a error 'entitysetname not set ' . What i'm wrong ?
In Response.ResponseText there is a JSON string with all data...

procedure TfrmPrestMac.btPrestClick(Sender: TObject);

procedure OnResponse(Response: TXDataClientResponse);
Var JFDBS: TJSObject; JManager: TJSObject; JTableList: TJSObject; JRowList: TJSArray;
begin
JFDBS := TJSObject(TJSObject(TJSJson.Parse(string(Response.Result)))['FDBS']);
JManager := TJSObject(JFDBS['Manager']);
JTableList := TJSObject(TJSArray(JManager['TableList'])[0]);
JRowList := TJSArray(JTableList['RowList']);

showmessage(Response.ResponseText);

//showmessage(js.ToString(JRowList));

wdPrest.Close();
//wdPrest.entitysetname:=js.ToString(JTableList);
wdPrest.SetJsonData(JRowList);
wdPrest.Open();

end;
begin
wdcPrest.RawInvoke(
'IMyQueryService.DoQuery',['',
'SELECT Azione, Data_Ora, TDS_Reg, Info_Cli FROM SMS_Act WHERE TDS_Reg > 200'],
@OnResponse);
end;

I created persistent fields and now go ... BUT now grid show me the rows of the records but they are Empty !! there is a wrong type data ?

Yes, you need to create persistent fields manually if you don't set EntitySetName.
As for grid being empty, inspect the request result and the objects if your code in OnResponse tries to read one single property wrongly, the result would be null. Make sure you are reading the returned JSON structure correctly.

persistent fields manually
by code ex. I need to put Original before ?

wdPrest.Close();
wdPrest.FieldDefs.Clear;
wdPrest.FieldDefs.Add('Original.Azione',ftString,5);
wdPrest.FieldDefs.Add('Original.Data_Ora',ftString,20);
wdPrest.FieldDefs.Add('Original.TDS_Reg',ftString,10);
wdPrest.FieldDefs.Add('Original.Info_Cli',ftString,200);
wdPrest.SetJsonData(JRowList);
wdPrest.Open();

But if i create field by code give me EntitySetName .dont set ...

if i create at design time fields dont give me error of EntitySetName but all records in grid are empty. I must put original in it ? where ? give me a cant find field ...

You should create the fields at design-time, at least to understand what's going on.
I can't tell why your grid is empty, your code looks correct according to the grid. Then I could only give you more information if I have more information, preferable a small sample project that reproduces the issue.

I'd like to add another voice to Guido's post...

If I have an XDataWebDataset dropped on a form, and then create fields at design time, and then try to populate it with the SetJsonData function, it works.

If I instead create the fields for the same control using FieldDefs at runtime, it seems to think it still needs the EntitySetName set and thus fails.

I think what he's trying to do (and what I'm trying to do) is to have a local TDataset that contains the records coming from something like an FDQuery on the server, without using the Entity mechanism but rather a service call. And as we can't use a local TFDMemTable (for reasons that are not clear to me), the XDataWebDataset seems the closest thing, particularly with the SetJsonData function beign availabe. But for this to work we need to be able to define the fields at runtime and do everything else without actually using the connectivity aspects of the XDataWebDataset in this instance as we're supplying the data after having gotten it (manually) via Json previously.

If there is a dummy field added at runtime then the error is different (can't find matching fields) so presumably there's a problem in XDataWebDataset's handling of FieldDefs. Maybe they're just not iimplemented?

Perhaps I can writeup an issue that more clearly shows the process from having an FDQuery dump data into a TStream on an XData endpoint and then having it recreated locally so it can be used in a local WebDBGrid or available in the usual TDataSet First/Next kind of mechanism. With the TStream and its JSON contents being used as a simple communications mechanism without having to pay much attention to how it works.

There is no "memory dataset" in Web Core because there is no direct memory access in JavaScript. It doesn't make sense.

TXDataWebDataset is indeed the correct "memory dataset" you can get in Web Core, because it supports JavaScript objects and primitive values, which is the closes you can get as "memory".

All you need to do is input raw data using SetJsonData, as you do. But of course you have to have your fields defined in advance, to tell the dataset which data from the objects you want to be treated as dataset field. Yes, I think more detail about what you are trying to achieve would be needed.