Using RESTFul Web Services with JSON

PowerBuilder DataWindow/DataStore/DataWindowChild (except for Composite, Crosstab, OLE 2.0, and RichText styles) can exchange JSON data with RESTFul Web services.

The JSONGenerator object constructs the JSON objects by adding values, objects, or arrays. JSONParser object loads the JSON data from a string or from a TXT file into a JSON object. JSONPackage merges data in a JSON object and extracts data from the JSON object. The data to be merged can be from DataWindow/DataStore/DataWindowChild, or from an existing JSON. The resulted JSON can be posted from the client to the server via HTTPClient, or retrieved from the server to the client via RESTClient. And to import JSON to or export JSON from the DataWindow control, DataStore object, or DataWindowChild object, you can use the DataWindow ImportJson/ImportJsonByKey/ExportJson functions.

The HTTPClient object sends HTTP requests and receives HTTP responses from a resource identified by a URI. Compared to the Inet object, HTTPClient is easier to use and supports more methods (Get/Post/Put/Delete) and more SSL protocols (TLS 1.0, TLS 1.1, TLS 1.2, SSL 2.0, and SSL 3.0). RESTClient object accesses the RESTful Web APIs and loads the JSON string returned from the RESTful Web APIs into the DataWindow object. The JSON string returned from the RESTFul Web Service APIs must have no more than 2 levels, and the top-level must be arrays, the second-level must be objects.

For more information about these objects and their functions/events/properties, see

the section called “JSONPackage object” in Objects and Controls

the section called “JSONGenerator object” in Objects and Controls

the section called “JSONParser object” in Objects and Controls

the section called “HTTPClient object” in Objects and Controls

the section called “RESTClient object” in Objects and Controls

the section called “ImportJson” in DataWindow Reference

the section called “ImportJsonByKey” in DataWindow Reference

the section called “ExportJson” in DataWindow Reference

the section called “CompressorObject object” in Objects and Controls

the section called “ExtractorObject object” in Objects and Controls.

Supported JSON formats

Plain JSON

A plain JSON follows the industry standard JSON format (as specified in https://www.json.org) and has one of the following structures depending on the actual export/import scenario.

A plain JSON can only contain elements of the following 4 data types: integer, string, boolean, and null.

One-level structure

A one-level plain JSON string must have only one level (cannot be two or more) and must be an object (cannot be an array).

This format is supported by the following methods (For all formats supported by a particular method, see Applicable methods):

Object

Method

DataWindow ImportRowFromJSON, ExportRowAsJson
RESTClient RetrieveOne

Here is an example of a one-level plain JSON string:

{ "column1":1, "column2":"name", "column3":true, "column4":null... }

Two-level structure

A two-level plain JSON must have its top-level as an array and the second-level as object(s) which represent a row of data.

[ SIMPLE-ROW1, SIMPLE-ROW2, SIMPLE-ROW3, SIMPLE-ROW4, SIMPLE-ROW5... ]

This format is supported by the following methods (For all formats supported by a particular method, see Applicable methods):

Object

Method

DataWindow ImportJson, ImportJsonByKey, ExportJson
JSONPackage GetValueToDataWindow, SetValueByDataWindow
RESTClient Retrieve, RetrieveOne, Submit

Here is an example of a two-level plain JSON string:

[{"emp_id":1,"emp_fname":"Fran","emp_lname":"Whitney", &
     "street":"9 East Washington Street","city":"Cornwall"}, &
 {"emp_id":2,"emp_fname":"Matthew","emp_lname":"Cobb", &
     "street":"7 Pleasant Street","city":"Grimsby"}, &
 {"emp_id":3,"emp_fname":"Philip","emp_lname":"Chin", &
     "street":"539 Pond Street","city":"Oakville"}, &
 {"emp_id":4,"emp_fname":"Julie","emp_lname":"Jordan", &
     "street":"1244 Great Plain Avenue","city":"Woodbridge"}, &
 {"emp_id":5,"emp_fname":"Robert","emp_lname":"Breault", &
     "street":"358 Cherry Street","city":"Milton"}, &
 {"emp_id":6,"emp_fname":"Melissa","emp_lname":"Espinoza", &
     "street":"1121 Apple Tree Way","city":"Iroquois Falls"}, &
 {"emp_id":7,"emp_fname":"Jeannette","emp_lname":"Bertrand", &
     "street":"2090A Concord Street","city":"Waterloo"}, &
 {"emp_id":8,"emp_fname":"Marc","emp_lname":"Dill", &
     "street":"897 Hancock Street","city":"Milton"}, &
 {"emp_id":9,"emp_fname":"Jane","emp_lname":"Francis", &
     "street":"127 Hawthorne Drive","city":"Scarborough"}, &
 {"emp_id":10,"emp_fname":"Natasha","emp_lname":"Shishov", &
     "street":"151 Milk Street","city":"Grimsby"}, &
 {"emp_id":11,"emp_fname":"Kurt","emp_lname":"Driscoll", &
     "street":"1546 School Street","city":"Grimsby"}, &
 {"emp_id":12,"emp_fname":"Rodrigo","emp_lname":"Guevara", &
     "street":"72 East Main Street","city":"Fort Henry"}]

DataWindow JSON

A DataWindow JSON is an object that contains elements representing the various aspects of a DataWindow.

This format is supported by the following methods (For all formats supported by a particular method, see Applicable methods):

Object

Method

DataWindow ImportJson, ImportJsonByKey, ExportJson
JSONPackage GetValueToDataWindow, SetValueByDataWindow
RESTClient Submit

The structure of DataWindow JSON is as follows:

{
"identity": "70c86603-983b-4bd9-adbc-259436e43cbd",
"version":1.0,
"platform":"PowerBuilder",
"mapping-method": 0,
"dataobject":{"name":"d_example",
    "meta-columns": [COLUMN-META1, COLUMN-META2…],
    "primary-rows": [DW-STANDARD-ROW1, DW-STANDARD-ROW2…],
    "filter-rows": [DW-STANDARD-ROW1, DW-STANDARD-ROW2…],
    "delete-rows": [DW-STANDARD-ROW1, DW-STANDARD-ROW2…],
    "dwchilds":{"department_id": [SIMPLE-ROW1,SIMPLE-ROW2…],
                "category_id": [SIMPLE-ROW1,SIMPLE-ROW2…]
                     …
                    }
}
}

Elements

Description

identity

A string identifying the format. Should keep unchanged.

version

An integer specifying the format version. Currently it is 1.0.

platform

A string specifying the platform where JSON string is generated. Values are: PowerBuilder, C#.

mapping-method

An integer specifying the method for mapping columns. Values are:

  • 0 -- Use the index of JSON item to map with the DataWindow column.

  • 1 -- Use the index of meta-columns to map with the DataWindow column.

  • 2 -- Use the key of JSON item to map with the DataWindow column.

Note: ImportJson function supports only 0 and 1, and ImportJsonByKey function and GetValueToDataWindow function ignore this value.

dataobject

An object indicating it is a DataWindow.

name

A string specifying the name of the DataWindow object (DataObject).

meta-columns (optional)

An array specifying the information of the DataWindow columns (excluding the computed columns).

For elements about the column meta information, see COLUMN-META below.

primary-rows (optional)

An array specifying the data row in the DataWindow primary buffer.

For elements about the DataWindow row, see DW-STANDARD-ROW below.

filter-rows (optional)

An array specifying the data row in the DataWindow filter buffer.

For elements about the DataWindow row, see DW-STANDARD-ROW below.

delete-rows (optional)

An array specifying the data row in the DataWindow delete buffer.

For elements about the DataWindow row, see DW-STANDARD-ROW below.

dwchilds (optional)

An object specifying the data in the DataWindowChild. The column name of the DataWindowChild is the key of dwchilds. The data row is SIMPLE-ROW. see SIMPLE-ROW below for more information.


COLUMN-META

COLUMN-META is an object that contains elements representing the various aspects of the DataWindow column (but not the computed column).

{
"name": "department_id",
"index": 1,
"datatype": "long", 
"nullable": 0
}

name -- (required) a string specifying the name of the column.

index -- (required) an integer specifying the sequence order of the column. This value will be used to map with the DataWindow column when the mapping-method value is set to 1.

datatype -- (required) a string specifying the type of the column. This value is not used by import.

nullable -- (required) an integer specifying whether to allow a null value. 0 - a null value is not allowed, 1 - a null value is allowed.

DW-STANDARD-ROW

DW-STANDARD-ROW is an object that contains elements representing the detailed information of the DataWindow row.

{
 "row-status": 0,
 "columns":{"column1": [CURRENT-VALUE, COLUMN-STATUS, ORIGINAL-VALUE],
           "column2": [CURRENT-VALUE, COLUMN-STATUS, ORIGINAL-VALUE],
            ...
          }
}

row-status -- (required) an integer specifying the status of the DataWindow row. 0 - NotModified!, 1 - DataModified!, 2 - New!, 3 - NewModified!.

columns -- (required) an object specifying the DataWindow column information including the current value, the column status, and the original value.

  • CURRENT-VALUE: (required) The current value of the column, in the following data type: integer, string, boolean, or null.

  • COLUMN-STATUS: (optional) An integer specifying the column status. 0 - (default) NotModified!, 1 - DataModified!.

  • ORIGINAL-VALUE: (optional) The original value of the column, in the following data type: integer, string, boolean, or null. The default type is null.

SIMPLE-ROW

SIMPLE-ROW is an object that contains elements representing the simple information of the DataWindow row. The data must be in the following data type: integer, string, boolean, or null.

{ "column1":1, "column2":"name", "column3":true, "column4":null... }

Example

Here is an example of a DataWindow JSON string:

{
  "identity": "70c86603-983b-4bd9-adbc-259436e43cbd",
  "version": 1,
  "platform": "PowerBuilder",
  "mapping-method": 0,
  "dataobject": {
    "name": "d_employee",
    "meta-columns": [
      {
        "name": "emp_id",
        "index": 0,
        "datatype": "long",
        "nullable": 1
      },
      {
        "name": "manager_id",
        "index": 1,
        "datatype": "long",
        "nullable": 1
      },
      {
        "name": "emp_fname",
        "index": 2,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "emp_lname",
        "index": 3,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "dept_id",
        "index": 4,
        "datatype": "long",
        "nullable": 1
      },
      {
        "name": "street",
        "index": 5,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "city",
        "index": 6,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "state",
        "index": 7,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "zip_code",
        "index": 8,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "phone",
        "index": 9,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "status",
        "index": 10,
        "datatype": "string",
        "nullable": 0
      },
      {
        "name": "ss_number",
        "index": 11,
        "datatype": "string",
        "nullable": 1
      },
      {
        "name": "salary",
        "index": 12,
        "datatype": "decimal",
        "nullable": 1
      },
      {
        "name": "start_date",
        "index": 13,
        "datatype": "date",
        "nullable": 1
      },
      {
        "name": "termination_date",
        "index": 14,
        "datatype": "date",
        "nullable": 1
      },
      {
        "name": "birth_date",
        "index": 15,
        "datatype": "date",
        "nullable": 1
      },
      {
        "name": "bene_health_ins",
        "index": 16,
        "datatype": "string",
        "nullable": 0
      },
      {
        "name": "bene_life_ins",
        "index": 17,
        "datatype": "string",
        "nullable": 0
      },
      {
        "name": "bene_day_care",
        "index": 18,
        "datatype": "string",
        "nullable": 0
      }
    ],
    "primary-rows": [
      {
        "row-status": 1,
        "columns": {
          "emp_id": [ 102 ],
          "manager_id": [ 501 ],
          "emp_fname": [ "Fran" ],
          "emp_lname": [ "Whitney" ],
          "dept_id": [ 400, 1, 100 ],
          "street": [ "49 East Washington Street" ],
          "city": [ "Needham" ],
          "state": [ "MA" ],
          "zip_code": [ "02192    " ],
          "phone": [ "6175554321", 1, "6175553985" ],
          "status": [ "A" ],
          "ss_number": [ "017349033" ],
          "salary": [ 50000, 1, 45700 ],
          "start_date": [ "1994-02-26" ],
          "termination_date": [ null ],
          "birth_date": [ "1966-06-05" ],
          "bene_health_ins": [ "Y" ],
          "bene_life_ins": [ "Y" ],
          "bene_day_care": [ "N" ]
        }
      },
      {
        "row-status": 0,
        "columns": {
          "emp_id": [ 129 ],
          "manager_id": [ 902 ],
          "emp_fname": [ "Philip" ],
          "emp_lname": [ "Chin" ],
          "dept_id": [ 200 ],
          "street": [ "59 Pond Street" ],
          "city": [ "Atlanta" ],
          "state": [ "GA" ],
          "zip_code": [ "30339    " ],
          "phone": [ "4045552341" ],
          "status": [ "A" ],
          "ss_number": [ "024608923" ],
          "salary": [ 38500 ],
          "start_date": [ "2005-08-04" ],
          "termination_date": [ null ],
          "birth_date": [ "1974-10-30" ],
          "bene_health_ins": [ "Y" ],
          "bene_life_ins": [ "Y" ],
          "bene_day_care": [ "N" ]
        }
      },
      {
        "row-status": 3,
        "columns": {
          "emp_id": [ 104, 1, null ],
          "manager_id": [ 902, 1, null ],
          "emp_fname": [ "Chris", 1, null ],
          "emp_lname": [ "Young", 1, null ],
          "dept_id": [ 200, 1, null ],
          "street": [ "57 Carver Street", 1, null ],
          "city": [ "Concord", 1, null ],
          "state": [ "MA", 1, null ],
          "zip_code": [ "12345    ", 1, null ],
          "phone": [ "6185551234", 1, null ],
          "status": [ "A", 1, null ],
          "ss_number": [ "010123456", 1, null ],
          "salary": [ 63000, 1, null ],
          "start_date": [ "2018-05-06", 1, null ],
          "termination_date": [ null ],
          "birth_date": [ "1984-10-12", 1, null ],
          "bene_health_ins": [ "Y", 1, null ],
          "bene_life_ins": [ "Y", 1, null ],
          "bene_day_care": [ null ]
        }
      }
    ],
    "filter-rows": [
      {
        "row-status": 0,
        "columns": {
          "emp_id": [ 148 ],
          "manager_id": [ 1293 ],
          "emp_fname": [ "Julie" ],
          "emp_lname": [ "Jordan" ],
          "dept_id": [ 300 ],
          "street": [ "144 Great Plain Avenue" ],
          "city": [ "Winchester" ],
          "state": [ "MA" ],
          "zip_code": [ "01890    " ],
          "phone": [ "6175557835" ],
          "status": [ "A" ],
          "ss_number": [ "501704733" ],
          "salary": [ 51432 ],
          "start_date": [ "2004-10-04" ],
          "termination_date": [ null ],
          "birth_date": [ "1959-12-13" ],
          "bene_health_ins": [ "Y" ],
          "bene_life_ins": [ "Y" ],
          "bene_day_care": [ "N" ]
        }
      }
    ],
    "delete-rows": [
      {
        "row-status": 0,
        "columns": {
          "emp_id": [ 105 ],
          "manager_id": [ 501 ],
          "emp_fname": [ "Matthew" ],
          "emp_lname": [ "Cobb" ],
          "dept_id": [ 100 ],
          "street": [ "77 Pleasant Street" ],
          "city": [ "Waltham" ],
          "state": [ "MA" ],
          "zip_code": [ "02154    " ],
          "phone": [ "6175553840" ],
          "status": [ "A" ],
          "ss_number": [ "052345739" ],
          "salary": [ 62000 ],
          "start_date": [ "1994-07-02" ],
          "termination_date": [ null ],
          "birth_date": [ "1968-12-04" ],
          "bene_health_ins": [ "Y" ],
          "bene_life_ins": [ "Y" ],
          "bene_day_care": [ "N" ]
        }
      }
    ],
    "dwchilds": {
      "dept_id": [
        {
          "dept_id": 100,
          "dept_name": "R & D"
        },
        {
          "dept_id": 200,
          "dept_name": "Sales"
        },
        {
          "dept_id": 300,
          "dept_name": "Finance"
        },
        {
          "dept_id": 400,
          "dept_name": "Marketing"
        },
        {
          "dept_id": 500,
          "dept_name": "Shipping"
        }
      ]
    }
  }
}

Applicable methods

The following table summarizes the methods that support the different JSON formats.

Objects

Methods

Supported JSON Formats

DataWindow

ImportRowFromJSON, ExportRowAsJson

1. Plain JSON: One-level structure

ImportJson, ImportJsonByKey

1. Plain JSON: Two-level structure

2. DataWindow JSON

ExportJson

1. Plain JSON: Two-level structure

2. DataWindow JSON

JSONPackage

LoadString, LoadFile

1. An object which contains a set of key/value pairs where key is the name of a JSONObjectItem-type object and the value for the key is a string, object, or array in the following formats: plain JSON, or DataWindow JSON.

GetValueToDataWindow

1. Plain JSON: Two-level structure

2. DataWindow JSON

3. An object which contains a set of key/value pairs where key is the name of a JSONObjectItem-type object and the value for the key is a string, object, or array in the following formats: plain JSON, or DataWindow JSON.

SetValueByDataWindow

1. Plain JSON: Two-level structure

2. DataWindow JSON

3. An object which contains a set of key/value pairs where key is the name of a JSONObjectItem-type object and the value for the key is a string, object, or array in the following formats: plain JSON, or DataWindow JSON.

RESTClient

Retrieve

1. Plain JSON: Two-level structure

RetrieveOne 1. Plain JSON: One-level structure & Two-level structure

Submit

1. Plain JSON: Two-level structure

2. DataWindow JSON

3. An object which contains a set of key/value pairs where key is the name of a JSONObjectItem-type object and the value for the key is a string, object, or array in the following formats: plain JSON, or DataWindow JSON.


See also

Plain JSON: One-level or Two-level structure

DataWindow JSON

Importing JSON data

Example 1 (using RESTClient)

If the JSON string returned from the RESTFul Web Service is a two-level plain JSON string, you can directly use the RESTClient object to get the data, as shown below.

restclient lnv_restclient
string ls_url
long ll_row
lnv_restclient = create restclient

ls_url = "https://rest.appeon.test/getemployees"
/* JSON string retruned from the url
[
{"Id":1106,"First_name":"Vincent","Last_name":"Phillipino","Sex":"Male","Age":63},
{"Id":1107,"First_name":"Natalie","Last_name":"Mariano","Sex":"Female","Age":16},
{"Id":1108,"First_name":"Li","Last_name":"Mary","Sex":"Female","Age":36},
{"Id":1109,"First_name":"Vic","Last_name":"Lu","Sex":"male","Age":20}
]*/
ll_row = lnv_restclient.retrieve(dw_1,ls_url)
destroy lnv_restclient
messagebox("Restclient","The rowcount of dw_1 = "+string(ll_row))

Example 2 (using JSONPackage, HTTPClient, & ImportJson)

If the JSON string returned from the RESTFul Web Service is not a perfect two-level plain JSON string, you may consider using the JSONPackage object to get the part of JSON string that is the plain JSON, and then use DataWindow ImportJson or ImportJsonByKey function to import the JSON data to the DataWindow.

string ls_value
string ls_url
string ls_json
long ll_return,ll_row
httpclient lnv_httpclient
jsonpackage lnv_pack1,lnv_pack2

lnv_pack1 = create jsonpackage
lnv_pack2 = create jsonpackage
lnv_httpclient = create httpclient

//Get the JSON string via httpclient
ls_url = "https://test.appeon.com/getfood"
ll_return = lnv_httpclient.sendrequest("Get",ls_url)
if ll_return <> 1 then
messagebox("Failed","SendRequest Failed:"+lnv_httpclient.getresponsestatustext( ))
return
end if
lnv_httpclient.getresponsebody( ls_json)
/* JSON string returned from the URL:
 '{
"id": "0001",
"type": "donut",
"name": "Cake",
"ppu": 0.55,
"batters":
           {
                    "batter":
                             [
                                       { "id": "1001", "type": "Regular" },
                                       { "id": "1002", "type": "Chocolate" },
                                       { "id": "1003", "type": "Blueberry" },
                                       { "id": "1004", "type": "Devil~'s Food"},
                       { "id": "5001", "type": "None" }                      
                             ]
           }
}'*/

//Load the JSON string via jsonpackage
lnv_pack1.loadstring(ls_json)
//Get the JSON string under key=batters
ls_value = lnv_pack1.getvalue("batters")
//Load the new JSON string via jsonpackage
lnv_pack2.loadstring( ls_value)
//Get the JSON data under key=batter (this json data meets the requirements by RestClient)
ls_value = lnv_pack2.getvalue( "batter")
//Import JSON data to the DataWindow via importjson
dw_1.importjson(ls_value)
destroy lnv_pack1
destroy lnv_pack2
destroy lnv_httpclient

Example 3 (using HTTPClient & JSONParser)

If the JSON string returned from the RESTFul Web Service is not at the required format, for example, the top-level are not arrays, or item contains null values, you may consider using the JSONParser and HTTPClient objects to import the JSON data to the DataWindow.

string ls_json
string ls_url
string ls_error
long ll_row,ll_return
long ll_root,ll_object,ll_item
long ll_loop1,ll_loop2
long ll_id,ll_data
string ls_data,ls_key
jsonparser lnv_jsonparser
httpclient lnv_httpclient

lnv_httpclient = create httpclient
lnv_jsonparser = create jsonparser
ls_url = "https://json.appeon.test/employees"

//Get the JSON string via httpclient
ll_return = lnv_httpclient.sendrequest("Get",ls_url)
if ll_return <> 1 then
messagebox("Failed","SendRequest Failed:"+lnv_httpclient.getresponsestatustext( ))
return
end if
lnv_httpclient.getresponsebody( ls_json)
/* JSON string returned from the URL:
{
"1106":{"First_name":"Vincent","Last_name":"Phillipino","Sex":"Male","Age":63},
"1107":{"First_name":"Natalie","Last_name":"Mariano","Sex":"Female","Age":16},
"1108":{"First_name":"Li","Last_name":"Mary","Sex":"Female","Age":36},
"1109":{"First_name":"Vic","Last_name":null,"Sex":"male","Age":20}
}*/

//Loads the JSON data via jsonpaser
ls_error = lnv_jsonparser.loadstring(ls_json)
if len(trim(ls_error))  > 0 then
Messagebox("Failed","Load json failed:"+ls_error)
return
end if
//Obtains the handle of root item
ll_root = lnv_jsonparser.getrootitem( )
//Obtains the each row in a loop
for ll_loop1 = 1 to lnv_jsonparser.getchildcount(ll_root)
ll_row = dw_1.insertrow(0)
//Obtains ID
ll_id = long(lnv_jsonparser.getchildkey(ll_root, ll_loop1))
dw_1.setitem( ll_row,"id", ll_id)
//Obtains the other column data in a loop
ll_object = lnv_jsonparser.getchilditem( ll_root, ll_loop1)
for ll_loop2 = 1 to lnv_jsonparser.getchildcount( ll_object)
           ll_item = lnv_jsonparser.getchilditem( ll_object, ll_loop2)
           ls_key = lnv_jsonparser.getchildkey( ll_object, ll_loop2)
           //Obtains the data type of each item
           choose case lnv_jsonparser.getitemtype( ll_item)
                    case jsonarrayitem!,jsonobjectitem!,jsonnullitem!
                             //ignores array, object and null item
                    case jsonstringitem!
                             ls_data = lnv_jsonparser.getitemstring(ll_object,ls_key)
                             dw_1.setitem(ll_row,ls_key,ls_data)
                    case jsonnumberitem!
                             ll_data = lnv_jsonparser.getitemnumber(ll_object,ls_key)
                             dw_1.setitem(ll_row,ls_key,ll_data)
                    case jsonbooleanitem!
                             //handles boolean as string
                             ls_data = string(lnv_jsonparser.getitemboolean( ll_object,ls_key))
                             dw_1.setitem(ll_row,ls_key,ls_data)
           end choose
next //Finish processing one row
next//Start processing next row
destroy lnv_jsonparser

Compressing and extracting data

Example 1 (using HTTPClient)

The HTTPClient object sends a request with a header "Accept-Encoding:gzip" which informs the RESTFul Web service that the client can extract data; then the Web service returns a compressed package and a response header "Content-Encoding: gzip" which indicates that the data is compressed; and then the ExtractorObject object extracts data from the package.

HttpClient   lhc_Client
ExtractorObject lnv_extractor
String     ls_Url, ls_id, ls_Method, ls_Body, ls_Respose
Long       ll_rtn
Blob       lb_body, lb_Extr
Boolean     ib_comp

lnv_extractor = Create ExtractorObject
lhc_Client = Create HttpClient

ls_Url = "https://demo.appeon.com/pb/webapi_client/department"
ls_Method = "GET"
lhc_Client.timeout = 10
lhc_Client.SetRequestHeader ( "Content-Type", "application/json" )
lhc_Client.SetRequestHeader("Accept-Encoding", "gzip")

ll_rtn = lhc_Client.sendrequest( ls_Method, ls_Url )
If ll_rtn = 1 Then
  ls_Respose = lhc_Client.Getresponseheaders( )
  If Pos (ls_Respose, "Content-Encoding: gzip"  ) > 0 Or Pos ( ls_Respose, "gzip" ) > 0 Then
    ib_comp = true
  End If
  If ib_comp Then
    // Extract the package
    ll_rtn = lhc_Client.GetResponsebody(lb_Body)
    If ll_rtn = 1 Then
      ll_rtn = lnv_extractor.Extract(lb_body, lb_Extr, ArchiveFormatGZip!)
      If ll_rtn = 1 Then
        ls_Body = String ( lb_Extr,EncodingUTF8! ) 
        MessageBox ( "Extract  Success", ls_Body )
      Else
        MessageBox ( "Extract Failed", "return:" + String (ll_rtn) )
      End If
    Else
      MessageBox ( "GetResponsebody Failed", "return:" + String (ll_rtn) )
    End If
  else
    // Extraction did not happen
    ll_rtn = lhc_Client.GetResponsebody( ls_Body,EncodingUTF8!)
    MessageBox ( "No Extract", ls_Body )
  End IF
Else
  MessageBox ( "SendRequest Failed", "Return:" + String ( ll_rtn ) )
End If

If IsValid (lnv_extractor) Then Destroy ( lnv_extractor )
If IsValid (lhc_Client) Then Destroy ( lhc_Client )

Example 2 (using RESTClient)

The RESTClient object sends a request with a header "Accept-Encoding:gzip" which informs the RESTFul Web service that the client can extract data; then the Web service returns a compressed package and a response header "Content-Encoding: gzip" which indicates that the data is compressed; and then the RESTClient object retrieves data from the compressed package.

RestClient   lrc_Client
String     ls_Url, ls_Method
Long     ll_rtn

lrc_Client = Create RestClient
ls_Url = "https://demo.appeon.com/pb/webapi_client/department"
ls_Method = "GET"
lrc_Client.SetRequestHeader ("Content-Type", "application/json")
lrc_Client.SetRequestHeader("Accept-Encoding", "gzip")

ll_rtn = lrc_Client.Retrieve( dw_1,ls_Url )
If ll_rtn >= 0 And lrc_Client.GetResponseStatusCode() = 200 Then
  MessageBox( "Retrieve Success","Rows:" + String ( ll_rtn ))
Else
  MessageBox( "Retrieve Failed","Rows:" + String ( ll_rtn ))
End If

If IsValid (lrc_Client) Then Destroy ( lrc_Client )

Example 3 (using OAuthClient)

The OAuthClient object sends a request with a header "Accept-Encoding:gzip" which informs the RESTFul Web service that the client can extract data; then the Web service returns a compressed package and a response header "Content-Encoding: gzip" which indicates that the data is compressed; and then the ExtractorObject object extracts data from the package.

OAuthRequest   loa_Request
OAuthClient loa_Client
ExtractorObject lnv_extractor
ResourceResponse   lrr_Response
Integer     li_rtn
Long       ll_rtn
String     ls_Body,ls_Response,ls_Token
Blob       lb_body, lb_Extr
Boolean     ib_comp

lnv_extractor = Create ExtractorObject
loa_Client = Create OAuthClient

ls_Token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE1NDZjNzliNzkyODVmYTJmMzZjY2Q3Mzg1OGE4MjY3IiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NDQ0MzMzMzAsImV4cCI6MTU0NDQzNjkzMCwiaXNzIjoiaHR0cDovL2NzaGFycHNlcnZlci5hcHBlb24uY29tOjYwMDAiLCJhdWQiOlsiaHR0cDovL2NzaGFycHNlcnZlci5hcHBlb24uY29tOjYwMDAvcmVzb3VyY2VzIiwiQXBwZW9uQXBpIl0sImNsaWVudF9pZCI6ImNsaWVudCIsInN1YiI6ImN1c3RvbV9jb2RlIiwiYXV0aF90aW1lIjoxNTQ0NDMzMzMwLCJpZHAiOiJsb2NhbCIsInNjb3BlIjpbIkFwcGVvbkFwaSJdLCJhbXIiOlsiY3VzdG9tX2NyZWRlbnRpYWxzIl19.XlGwMqVRwJ_4gkIbNaK_HX6_0hvWE0EJXciurkNjqdOZegF_QQYTJp3jBA1idtMC_lB24TurZM1JSfbTXv4ZSQVdCTk3p5kyV8UTqpDUKbu73HQoPNDlXuTyQb58rGVRGC4bp7weLlpUqrQT2OB8PT2N_JCWtTnrwNToNsc4H1e2NNvNUoe90May7ICs2ovofQ37FQG7IwLSoe_aUsS-8togNxQ1SxsdR7__Amb0G0Asu8QaRTIzomerDGX9Ct_yt6cgz-3Z7jR9Eb1QFaZxr_PALwMVIHVmHJK58GCePGQ0nivJCYMO4WEhysme_Thics4cx_EKl4T8t0VHmcqCNw"
loa_Request.SetHeader("Accept-Encoding", "gzip")
li_rtn = loa_Request.SetAccessToken (ls_Token)
loa_Request.Method = "GET"
loa_Request.Url = "https://demo.appeon.com/pb/webapi_client/identity/departments"
li_rtn =loa_Client.RequestResource( loa_Request, lrr_Response )
If li_rtn = 1 Then
  ls_Response = lrr_Response.getheaders( )
  If Pos (ls_Response, "Content-Encoding: gzip"  ) > 0 Or Pos ( ls_Response, "gzip" ) > 0 Then
    ib_comp = true
  End If
  If ib_comp Then
    // Extract the package
    ll_rtn = lrr_Response.getbody( lb_body)
    If ll_rtn = 1 Then
      ll_rtn = lnv_extractor.Extract(lb_body, lb_Extr,ArchiveFormatGZip!)
      If ll_rtn = 1 Then
        ls_body = String ( lb_Extr,EncodingUTF8! ) 
        MessageBox( "Extract Success", String ( Len ( ls_body ) ) )
      Else
        MessageBox( "Extract Failed", "return:" + String (ll_rtn) )
      End If
    Else
      MessageBox( "Getbody Failed", "return:" + String (ll_rtn) )
    End If
    
  else
    // Extraction did not happen
    ll_rtn = lrr_Response.getbody( ls_Body)
    MessageBox( "No Extract", ls_Body )
  End IF
Else
  MessageBox ( "RequestResource Failed","RequestResource Return:" + String ( li_Rtn ) )
End If

If IsValid ( loa_Client ) Then Destroy ( loa_Client )
If IsValid ( lnv_extractor ) Then Destroy ( lnv_extractor )