Icon View Thread

The following is the text of the current message along with any replies.
Messages 1 to 7 of 7 total
Thread JSON Webservice
Mon, Jul 3 2017 2:10 PMPermanent Link

Big Al

I need to access a webservice that passes back JSON.

Could someone show me example code to access the webservice and return JSON  into a grid, or if I'm expecting back just one row for data, a way to load into an array or something so that I can reference the values that are being returned?

Pretend the webservice url is http://www.sample.com/webservice?action=getuser&userid=34
which gives me back one datarow, or http://www.sample.com/webservice?action=getuser
which gives me back many rows of data.

I'm making progress on my ewb learning but some things are still very cloudy to me.

Thanks
Big Al
Mon, Jul 3 2017 5:15 PMPermanent Link

Mark Brooks

Slikware

Avatar

Hi

Can I ask, are you happy calling the API and getting something back i.e. Is your issue with parsing the JSON or getting it back in the first place?

Mark
Mon, Jul 3 2017 6:18 PMPermanent Link

Big Al

>Mark Brooks wrote:

>Hi

>Can I ask, are you happy calling the API and getting something back i.e. Is your issue with parsing the JSON or >getting it back in the first place?

I don't have a problem in getting it to return the data, but I don't know how to call it properly in ewb. If I call it from the URL in a browser it returns the data.

So once I understand how to request the data in ewb, I would like to know how to load it into a grid and also possibly load it into variables or an array if I am getting back just one row of data.

Does that help?
Big Al
Mon, Jul 3 2017 9:24 PMPermanent Link

Raul

Team Elevate Team Elevate

On 7/3/2017 2:10 PM, Big Al wrote:
> Could someone show me example code to access the webservice and return JSON  into a grid, or if I'm expecting back just one row for data, a way to load into an array or something so that I can reference the values that are being returned?

if JSON being returned is in the EWB format then all of this is trivial
and you can just read up on datasets in manual.

I will assume it's not in the EWB dataset format and you have to parse
it yourself and then populate grid etc.

If you know it's single row you could parse it into a a class or array
of 1 element. If it's array of values then loading it into array or list
of some sort works. Then it's available in the app.

There are number of posts in these newsgroups already and some of the
samples include this as well (like login one) but i'll try to do a
really quick version below since it's nice to have it all in a single post.

> Pretend the webservice url is http://www.sample.com/webservice?action=getuser&userid=34
> which gives me back one datarow, or http://www.sample.com/webservice?action=getuser
> which gives me back many rows of data.

This does not appear to be a real webservice.  URL itself is not really
relevant as long as (1) you know what the endpoint is (which you do) and
that (2) web service allows cross origin access for cases when your app
is NOT served from the exact same web server that service is hosted on.

In general you would do a normal TServerRequest to the service endpoint
and then once you get your RequestComplete event and all is OK (i.e.
status code HTTP_OK) you'd have the response in the
Request.ResponseContent.Text property and can parse it.

For the example below i'll just load the JSON from multilineedit - using
serverrequest is well documented in manual so you should be able to get
to the point of having json data in the app as input easily


What is more important is what does the JSON look like. For this sample
I'll assume it looks like this :

{"UserID": 1,"Name": "John Smith","Age":40,"DriversLicense": false}

In order to parse this you want to create a custom class that descends
from TPersistent.

I created this

  TUser = class(TPersistent)
      private
         FUserID:integer;
         FName:string;
         FAge:integer;
         FDriversLicense:boolean;
         FReader:TReader; //own copy
      published
         property UserID: integer read FUserID write FUserID;
         property Name: String read FName write FName;
         property Age: integer read FAge write FAge;
         property DriversLicense: boolean read FDriversLicense write
FDriversLicense;
      public
        constructor Create; override;
         destructor Destroy; override;
         procedure ParseJSON(const JSONData:string);
      end;


Note that i like to include treader in in the class despite bit of
overhead - you can have one in the main app that is shared.


and implementation of the few functions is fairly trivial


constructor TUser.Create;
begin
   inherited Create;
   FReader := TReader.Create;
end;

destructor TUser.Destroy;
begin
   FReader.Free;
   inherited Destroy;
end;

procedure TUser.ParseJSON(const JSONData:string);
begin
   FReader.Initialize(JSONData);
   Load(FReader);
end;



and then to actually use all of this once you have your JSON is this

procedure TForm1.Button1Click(Sender: TObject);
var
   user:TUser;
begin
   user := TUser.Create;
   user.parseJSON(MultiLineEditJSONInput.lines.text);
   MultiLineEditLogging.Lines.Add('User ID = ' + inttostr(user.userID));
   user.free;
end;

and output will be

User Name = John Smith


You should be catching exceptions and deal with errors of course but
that is left out since it's specific to your implementation.


For the case of multiple rows I'll assume you get an array back of same
user json data. For example this (note the "users" as the name):

{ "Users":
  [
    {"UserID": 1,"Name": "John Smith","Age":40,"DriversLicense": false},
    {"UserID": 2,"Name": "Mary Jones","Age":30,"DriversLicense": true}
  ]
}

For this you need to do bit more work - new class that contains an array
and more specifically it overrides the LoadProperty and LoadArrayElement
functions.

  TUsers = class(TPersistent)
    private
      FUserArray: array of TUser;
      FReader: TReader;
    protected
      function LoadProperty(AReader: TReader): Boolean; override;
      function LoadArrayElement(AReader: TReader): Boolean; override;
    published
      property Users: array of TUser read FUserArray;
    public
      constructor Create; override;
      destructor Destroy; override;

      procedure ParseJSON(const JSONData:string);
  end;


and implementation


constructor TUsers.Create;
begin
   inherited Create;
   SetLength(FUserArray,0);
   FReader := TReader.Create;
end;

destructor TUsers.Destroy;
begin
   SetLength(FUserArray,0);
   FReader.Free;
   inherited Destroy;
end;

function TUsers.LoadProperty(AReader: TReader): Boolean;
var
  TempPropertyName: String;
begin
  if SameText(AReader.GetPropertyName,'Users') then
   begin
     Result := True;
     AReader.SkipPropertyName;
     AReader.SkipPropertySeparator;
     SetLength(FUserArray,0);
     LoadArray(AReader);
   end
 else
   Result := inherited LoadProperty(AReader);
end;

function TUsers.LoadArrayElement(AReader: TReader): Boolean;
begin
  SetLength(FUserArray,Length(FUserArray) + 1);
  FUserArray[Length(FUserArray) - 1] := TUser.Create;
  FUserArray[Length(FUserArray) - 1].LoadObject(AReader);
  Result := True;
end;

procedure TUsers.ParseJSON(const JSONData:string);
begin
   FReader.Initialize(JSONData);
   Load(FReader);
end;

and then to actually use this

procedure TForm1.Button1Click(Sender: TObject);
var
   i:integer;
   users:TUsers;
begin
   users := TUsers.Create;
   users.parseJSON(MultiLineEditJSONInput.lines.text);

   MultiLineEditLogging.Lines.Add('Num users = ' +
inttostr(length(users.Users)));
   for i:= 0 to length(users.Users)-1 do
   begin
      MultiLineEditLogging.Lines.Add('User Name = ' + users.Users[i].name);
   end;

   users.free;
end;


and output is this :

Num users = 2
User Name = John Smith
User Name = Mary Jones


Note that all of of the Tuser and Tusers classes can go into separate
units so you can reuse them easily.
Also the treader can be removed from tuser if you deal with tusers.

Hope it helps.

Raul
Mon, Jul 3 2017 10:06 PMPermanent Link

Big Al

>Raul wrote:


>I will assume it's not in the EWB dataset format and you have to parse
>it yourself and then populate grid etc.

Raul, thanks for the detailed information. I will try to understand it.

The Webservice is under my control, and the actual website will be the same domain name as is my ewb app.

I don't know the difference between ewb JSON data and regular JSON data but I think I will try to see what the difference is, and format the data coming from the webservice to look like ewb would so that it would make it much easier on ewb.

I have not looked at datasets yet, but is there anywhere where I can find the difference between ewb JSON dataset and regular JSON data???

Thanks again for everyone's help.

Big Al
Mon, Jul 3 2017 10:08 PMPermanent Link

Big Al

Here is an example of the JSON data when it's just one row if that helps.

{"rows":[{"row":{"PersonID":"98","EMail":"sdea@ding.com","FirstName":"","LastName":"","DefaultLocation":null,"DefaultHomePage":"Default","UserID":"62DAF6","UserType":"User","NumSearches":"0","NumMessages":"0","NumUnreadMsgs":"0","NumFavorites":"0","NumListings":"0","NumNewsletters":"0","NumLocations":"0","NumMembers":"0"}}]}
Mon, Jul 3 2017 10:23 PMPermanent Link

Raul

Team Elevate Team Elevate

On 7/3/2017 10:06 PM, Big Al wrote:
> The Webservice is under my control, and the actual website will be the same domain name as is my ewb app.
> I don't know the difference between ewb JSON data and regular JSON data but I think I will try to see what the difference is, and format the data coming from the webservice to look like ewb would so that it would make it much easier on ewb.

Al,

if you wish to use EWB tdataset then it expects the JSON to look certain
way - the reference is here :
https://www.elevatesoft.com/manual?action=viewtopic&id=ewb2&topic=JSON_Reference

This specific JSON format has "built-in" EWB support - meaning you can
just "load" data into tdataset
(https://www.elevatesoft.com/manual?action=viewtopic&id=ewb2&topic=Creating_Loading_DataSets)
and it "knows" how to parse it and populate the dataset.

Tim basically has done the work for you (that i showed in previous post)
so all you have to do is call "LoadRows(UsersData);" or such in your
code (assuming UsersData is setup as proper dataset with columns etc).

Raul

Image