Consuming complex requests are possible in aperïo. There are some limitations which comes from performance assumptions. Total memory per job is 16MB. As Iptor is focused on performance of aperïo the JSON parser is created as “direct indexing parser”, not DOM parser. DOM parser was much more slower in usage by RPG programs. NOTE: In the road map is to prepare full DOM JSON parser and possibility for programer to decide which one is used in aperïo. Currently the number of incoming parameters are limited to 1024.
The incoming jSON is indexed and the indexes are put into predefined 1024 array. Procedure reqGetValue() allows programmer to get value for any named element of JSON.
Example 1:
{
"IptorAPI":"1.0",
"method":"sourcingPolicyAvailability.get",
"params":{
"item":"STENE1",
"customer":"MEH001",
"order":111655,
"warehouse":"MOB"
},
"id":"js0clvwr"
}
The JSON above is indexed and to get value of each element programmer needs only one command:
//to get value of parameter "item"
xxValue = reqGetValue('params':'item');
//to get value of "method"
xxValue = reqGetValue('method');
//to get entire JSON string object of "params"
xxStringValue = reqGetValue('params': *omit: *omit: *omit: xxPointer: xxLen);
//Then in xxStringValue contains "{"item":"STENE1","customer":"MEH001","order":111655,"warehouse":"MOB"}"
//But the String value is limited to 1024 characters
//the xxPointer contains pointer to memory where "params" value starts....
//The xxLen contains length in bytes the "params" value
//By using xxPointer and xxLen programmer can copy even long text and parse it once again
Then how to read more complex requests. If the number of parameters do not exceed 1024 then it could be done without any additional parsing JSON. If the request is more complex then it may required additional parsing process.
Example 2:
{
"IptorAPI":"1.0",
"method":"salesOrders.get",
"params":{
"orders":[
12345,
56789,
23476,
255433
]
},
"id":"js0clvwr"
}
The JSON above is indexed and to get value of each element programmer needs only one command:
//to get number of elements in "order" array
xxNum = reqGetNumOfElements('params':'orders[]');
//to get first element of array
xxOrder = reqGetValue('params':'orders[1]');
//to get third element of array
xxOrder = reqGetValue('params':'orders[3]');
Example 3:
{
"IptorAPI":"1.0",
"method":"salesOrders.update",
"params":{
"order":12345,
"deiveryDate":"2019-02-22",
"discounts":[
{
"discountId":"ID1",
"percentage":10
},
{
"discountId":"ID2",
"amount":2
}
],
"texts":{
"textLine1":"bla 123",
"textLine2":"acb 123"
}
},
"id":"js0clvwr"
}
To get values foe ach element You can use following commands in RPG program:
//to get number of elements in "discounts" array
xxNum = reqGetNumOfElements('params':'discounts[]');
//to get discountId from first element of array
xxDiscountId = reqGetValue('params':'discounts[1]:discountId');
//to get percentage from first element of array
xxDiscountId = reqGetValue('params':'discounts[1]:percentage');
//to get textLine1 value
xxText = reqGetValue('params':'texts:textLine1');
//etc...
If the incoming JSON is more complex then it can be parsed partially in RPG program. First, programmer must get small portion of incoming JSON into variable, then parse it.
Example 4:
{
"IptorAPI":"1.0",
"method":"salesOrders.update",
"params":{
"order":12345,
"currency": "EUR",
"lines":[
{
"line":10,
"quantity":3,
"price":12.57,
"unit":"EACH"
},
{
"line":20,
"quantity":5,
"unit":"BOX10"
}
],
"texts":{
"textLine1":"bla 123",
"textLine2":"acb 123"
}
},
"id":"js0clvwr"
}
Programmer can get one line information into variable then parse it.
//get first line JSON string
xxOneLine = reqGetValue('params': 'lines[1]');
//Create parsed JSON objects in memory
xxJSONPointer = JSON_parse(%ADDR(xxOneLine): %LEN(xxOneLine));
//to get price for line
xxPrice = JSON_getValue(xxJSONPointer: %UCS2('price'));
//Do not forgot to destroy parsed objects from memory
JSON_parserDestroy(xxJSONPointer);
//get second line JSON string
xxOneLine = reqGetValue('params': 'lines[2]');
//Create parsed JSON objects in memory
xxJSONPointer = JSON_parse(%ADDR(xxOneLine): %LEN(xxOneLine));
//to get quantity for line
xxQuantity = JSON_getValue(xxJSONPointer: %UCS2('quantity'));
...
//Do not forgot to destroy parsed objects from memory
JSON_parserDestroy(xxJSONPointer);
Another example of reading complex request. This time request contains array with item and price as element of table See IAF100AP/QSAMPLES member: EXAMPLE023
{
"method":"aa_test",
"params":{
"arrayParams":[
{"item":"sampleItem1","price":123.45},
{"item":"sampleItem2","price":67.89}
]
}
}
****************************************************************
* Subprocedure: lcl_processRequest **
****************************************************************
P lcl_processRequest...
P B
D PI 9B 0
D lNumOfElem S 10i 0
D lElem S 10i 0
D lItem S 35
D lPrice S 30 9
/FREE
monitor;
// USER CODE begin - entry parameters validation
if reqExist('params':'arrayParams[]');
lNumOfElem = reqGetNumOfElements('params' :'arrayParams[]');
else;
return API_PARAMS_ERR;
endif;
lElem = 1;
dow lElem <= lNumOfElem;
lItem = *blanks;
lPrice = 0;
if reqExist('params':'arrayParams['+%trim(%char(lElem))+']:item');
lItem = reqGetValue( 'params'
:'arrayParams['+%trim(%char(lElem))+']:item');
endif;
if reqExist('params':'arrayParams['+%trim(%char(lElem))+']:price');
lPrice = %dec(reqGetValue( 'params'
:'arrayParams['+%trim(%char(lElem))+']:price') :17 :4);
endif;
//Put received value into response
if lItem <> *blanks and lPrice <> 0;
respAddElement('items' :'data' :'array');
respAddStringField('items' :'receivedItem' :lItem);
respAddNumericField('items' :'receivedPrice' :lPrice);
endif;
lElem += 1;
enddo;
// USER CODE end - entry parameters validation
// Add data messages into response
XPERRC = lcl_addMessages();
on-error;
return API_PARAMS_ERR;
endmon;
// Return
return XPERRC;
/END-FREE
P E
There is also posible to use other parsers not only included in aperio. Here is an example of using SQL parser to receive the same request See IAF100AP/QSAMPLES member: EXAMPLE023
{
"method":"aa_test",
"params":{
"arrayParams":[
{"item":"sampleItem1","price":123.45},
{"item":"sampleItem2","price":67.89}
]
}
}
****************************************************************
* Subprocedure: lcl_processRequest **
****************************************************************
P lcl_processRequest...
P B
D PI 9B 0
D lItem S 35
D lPrice S 30 9
D lTmpTxt_ptr S * inz(*null)
D lTmpTxt S 16000C based(lTmpTxt_ptr)
D lParams S 16000C varying
D lPointer S *
D lLen S 10u 0
D lType S 1A
D lExist S N
/FREE
monitor;
// USER CODE begin - entry parameters validation
//Get entire json object (in this case array) 'params.arrayParams'
//As we ask to get part of JSON object from request, the
// reqGetValue will not return any value. But put the result in
// lPointer and lLen.
reqGetValue( 'params': 'arrayParams' :lType :lExist
:lPointer :lLen);
//Here lTmpTxt points to the place in memory where request array exists
//NOTE: lTmpTxt starts from pointer provided by reqGetValue but
// the length ot lTmpTxt is 16000 char. So, it may happen that variable
// lTmpTxt covers also other important structures in memory.
lTmpTxt_ptr = lPointer;
//To avoid to destroy other structures in memory we intentionally
// do not use lTmpTxt as variable. First we have to copy from lTmpTxt
// only part which is reserved for our request array. Variable lLen
// contains size of value in bytes. Assuming request is kept in
// DBCS (2 bytes per character) we use div(lLen :2) to calculate
// current length of value in characters.
lParams = %subst(lTmpTxt :1 :%div(lLen :2));
//Parse array using SQL. We use table in qtmp to temporary store
//array results..
EXEC SQL
create table qtemp/myRequest(jsondoc varchar(3200) CCSID 1208);
EXEC SQL
insert into myRequest values :lParams;
EXEC SQL
declare C1 cursor for
SELECT t.item, t.price
FROM myRequest,
JSON_TABLE(myRequest.jsondoc, 'lax $'
COLUMNS (
item VARCHAR(35) PATH 'lax $.item',
price float PATH 'lax $.price'
)
)as t;
exec SQL open C1;
exec SQL FETCH C1 INTO :lItem, :lPrice;
dow sqlcod = *zero;
//Put received values into respnse
if lItem <> *blanks and lPrice <> 0;
respAddElement('items' :'data' :'array');
respAddStringField('items' :'receivedItem' :lItem);
respAddNumericField('items' :'receivedPrice' :lPrice);
endif;
exec SQL FETCH C1 INTO :lItem, :lPrice;
enddo;
exec SQL CLOSE c1;
EXEC SQL
drop table myRequest;
// USER CODE end - entry parameters validation
// Add data messages into response
XPERRC = lcl_addMessages();
on-error;
return API_PARAMS_ERR;
endmon;
// Return
return XPERRC;
/END-FREE
P E