Working With JSON in RPG
Working With JSON in RPG
Scott Klement
http://www.scottklement.com
The Agenda
Agenda for this session:
1. What is JSON?
• Why use JSON?
• Syntax Overview
2
Ugggh, Another Thing to Learn!
JSON is self-describing (field names are in the data itself) and human-readable.
Much Like XML
4
Much Different Than XML
JSON is simpler:
• only supports UTF-8, whereas XML supports a variety of encodings.
• doesn't support schemas, transformations.
• doesn't support namespaces
• method of "escaping" data is much simpler.
JSON is faster
• more terse (less verbose). About 70% of XML's size on average
• simpler means faster to parse
• dead simple to use in JavaScript
Have you noticed that people are rarely discussing XML anymore?
• Google, Facebook, Twitter, IBM Watson focus on JSON
• JSON has become the most popular for REST APIs
• JSON has become the de-facto standard for Internet of Things (IoT)
• XML is still used, but mainly in pre-existing applications. Rarely in new projects.
6
JSON Evolved from JavaScript
Originally JSON was the language used to describe "initializers" for JavaScript
objects.
• Used to set the initial values of JavaScript Objects (data structures), and arrays.
Even for arrays nested in data structures or vice-versa.
Data can be nested (arrays inside objects and/or objects inside arrays).
8
JSON and XML to Represent a DS
[
{
"custno": 1000, This is how the same
"name": "ACME, Inc" array might be
}, represented (with data
{ inside) in a JSON
"custno": 2000, document.
"name": "Industrial Supply Limited"
}
]
<list>
<cust>
<custno>1000</custno> And it’s approximately
<name>Acme, Inc</name> the same as this XML
</cust>
document.
<cust>
<custno>2000</custno>
<name>Industrial Supply Limited</name>
</cust>
</list> 9
<list><cust><custno>1000</custno><name>ACME, Inc</n
ame></cust><cust><custno>2000</custno><name>Industr 142 bytes
ial Supply Limited</name></cust></list>
11
YAJL Provides
I have found the tree-style routines to be easier to work with, so will use
them in my examples.
12
DATA-GEN (Preview)
end-ds; }
File = '/tmp/example.json';
DATA-GEN invData %DATA(File: 'doc=file output=clear countprefix=num_')
%GEN('YAJLDTAGEN');
{
"success": false,
"errmsg": "Error Message Here",
"list": [ ]
}
To keep it simple, we'll just have it write the result to an IFS file.
Though, you can also use this in a web service, if desired (code download
from ScottKlement.com will have an example of this)
15
D row ds qualified
D inv 5a The BNDDIR and copy
D date 8s 0 book are needed to
D name 25a access YAJL's routines
D amount 9p 2
D weight 9p 1
D success s 1n
D errMsg s 500a varying
16
RPG Writing JSON -- Mainline
To generate the JSON data we'll use the following YAJL procedures:
begsr JSON_Start;
yajl_beginObj();
yajl_addBool('success': success );
yajl_addChar('errmsg': errMsg );
yajl_beginArray('list');
endsr;
{ yajl_beginObj
"success": false, yajl_addBool
"errmsg": "Error Message Here", yajl_addChar
"list": [ yajl_beginArray
19
JSON_addRow Routine
begsr JSON_addRow;
yajl_beginObj(); {
yajl_addChar('invoice': row.inv ); "invoice": "XYX",
yajl_addChar('date': dateUsa ); "date": "12/31/2013",
yajl_addChar('name': %trim(row.name)); "name": "John Doe",
yajl_addNum('amount': %char(row.amount)); "amount": 123.45,
yajl_addNum('weight': %char(row.weight)); "weight": 100.5
yajl_endObj(); }
endsr;
Each time this runs, it adds a new JSON element to the end of the document.
Since we have not yet called YAJL_endArray(), each object is a new element in the array that
was started in the JSON_start subroutine.
20
JSON_Finish & JSON_Save
begsr JSON_Finish;
yajl_endArray(); Finish off the array and
yajl_endObj(); the object that began in
JSON_start.
endsr;
begsr JSON_Save;
21
{
"success": true,
"errmsg": "",
"list": [
{
"invoice": "70689",
"date": "09/01/2010",
Result with yajl_genOpen(*ON)
"name": "JIM JOHNSON",
"amount": 14.80, ("pretty" JSON data)
"weight": 3.5 Includes line breaks and indenting to make
}, it easy as possible for humans to read.
{ This extra formatting isn't needed for
"invoice": "70695", computer programs to read it, however.
"date": "09/01/2010",
"name": "BILL VIERS",
"amount": 9.80,
"weight": 3.2
}
]
}
22
RPG Writing JSON – "Compact" output
{"success":true,"errmsg":"","list":[{"invoice":"70689","date":"09/01/
2010","name":"JIM JOHNSON","amount":14.80,"weight":3.5},{"invoice":"7
0695","date":"09/01/2010","name":"BILL VIERS","amount":9.80,"weight":
3.2}]}
23
Although there isn't time to go into detail about how to code RESTful
web services in this presentation, the gist would be:
• Get input parameters from the URL.
• Create the JSON document in exactly the same way.
• Use YAJL_writeStdout() instead of YAJL_saveBuf()
YAJL_writeStdout() writes the JSON data to standard output with
HTTP headers, as would be needed if writing your own web service
provider to be run through the IBM HTTP Server (powered by Apache.)
For consuming web services, you can use YAJL_copyBuf() or
YAJL_copyBufStr() which returns the JSON data into a buffer (pointer)
or RPG string so that you can pass it to HTTPAPI or another HTTP
tool to send it.
Examples are provided in the sample code on Scott's web site, here:
http://www.scottklement.com/yajl
24
Reading JSON Data With DATA-INTO
DATA-INTO is an RPG opcode that was added to IBM i 7.2 and newer
releases.
The following link describes the PTFs needed for DATA-INTO support
on 7.2 and 7.3 releases:
http://ibm.biz/data-into-rpg-opcode-ptfs
YAJL supports DATA-INTO as of the April 2018. (But, get the latest
copy with the latest enhancements!)
25
What is DATA-INTO?
26
DATA-INTO Syntax
The DATA-INTO opcode syntax is:
DATA-INTO result %DATA(document[:options])
%PARSER(parser[:options]);
result = RPG variable (data structure) that data will be loaded into
document = the XML document, or IFS path to the XML document.
end-ds; // }
field names must match, objects must match a data structure, arrays must 28
match an array.
YAJLINTO Parser
Example of DATA-INTO with YAJLINTO as the Parser:
DATA-INTO result %DATA( '/tmp/example.json'
: 'doc=file case=any countprefix=num_')
%PARSER('YAJLINTO');
result – the name of RPG data structure that I want to load the JSON
into. You can name it whatever you like on your DCL-DS.
/tmp/example.json - IFS path to the JSON document we generated
doc=file – tells RPG to read the document from a file (vs. a variable)
case=any – tells RPG that the upper/lower case of variable names
does not have to match the document
countprefix=num_ – any variables in the DS that start with "num_"
should receive counts of matching fields. For example,
"num_list" would give the number elements in the "list" array.
29
YAJLINTO Example (1 of 2)
**free
ctl-opt DFTACTGRP(*NO) OPTION(*SRCSTMT) BNDDIR('YAJL');
/include yajl_h
end-ds;
30
YAJLINTO Example (2 of 2)
dcl-ds printme len(132) end-ds;
dcl-s i int(10);
dcl-s dateISO date(*ISO);
for i = 1 to result.num_list;
dateISO = %date(result.list(i).date:*USA);
printme = result.list(i).invoice + ' '
+ %char(dateISO:*ISO) + ' '
+ result.list(i).name + ' '
+ %editc(result.list(i).amount:'L') + ' '
+ %editc(result.list(i).weight:'L');
write QSYSPRT printme;
endfor;
*inlr = *on;
31
YAJLINTO Output
The output of the program would look as follows (goes to the spool, I
didn't take the time to add headings, etc)
32
Data-Into from a Web Service
If you need to read JSON from a web service, the JSON may be provided to
you in two ways:
• some tools provide JSON as a string (usually parameter) to your program
• some tools (such as the IBM HTTP server (powered by Apache)) send the
data via “standard input”
To read data sent in a character string, use doc=string (just as you would with
XML-INTO)
data-into result %DATA( myJsonString
: 'doc=string case=convert countprefix=num_')
%PARSER('YAJLINTO');
Since September 2018, YAJLINTO supports direct reading from standard input by
passing the special value *STDIN. This makes it easy to get input via Apache.
This talk will discuss the tree-based method, since I have found it
much easier to use.
34
Populating the YAJL tree
To load JSON data from IFS into the tree parser, call
yajl_stmf_load_tree(), as follows:
35
amount (number)
array index 2
name (char string)
(object)
Given any node, I can
retrieve it's "children". amount (number)
and with the node
So with docNode, I can get for 'list', I can get weight (number)
the nodes for the 'success', the array
'errmsg' and 'list' elements. elements, etc 36
Retrieving A "Child Node"
// { "success": true }
if yajl_is_true( succNode );
// success!
else;
// failure
endif;
37
if yajl_is_false( succNode );
errMsgNode = yajl_object_find( docNode: 'errmsg' );
msg = yajl_get_string(errMsgNode);
// msg now contains "invalid start date"
endif;
38
Processing an Array
i = 0;
dow YAJL_ARRAY_LOOP( list: i: node );
enddo;
39
i = 0;
dow YAJL_OBJECT_LOOP( docNode: i: key: val );
enddo;
40
Freeing Up Resources (When Done)
yajl_tree_free( docNode );
41
To put together all of the YAJL tree concepts shown in the preceding
slides, I have provided an RPG example.
• Reads the same JSON file (from IFS) that we created earlier
• Loads the JSON data into an RPG data structure.
• After all is loaded, loops through and prints the data (just to
demonstrate reading)
42
RPG Reading JSON (1 of 6)
/include yajl_h
D list_t ds qualified
D template
D inv 5a
D date 8s 0
D name 25a The 'result' data
structure will be
D amount 9p 2
populated from the
D weight 9p 1 JSON data
D result ds qualified
D success 1n
D errmsg 500a varying
D list likeds(list_t) dim(999)
D i s 10i 0
D j s 10i 0
D dateUSA s 10a
D errMsg s 500a varying inz('') 43
D docNode s like(yajl_val)
D list s like(yajl_val)
D node s like(yajl_val)
D val s like(yajl_val)
45
j = 0; yajl_object_loop is called
dow YAJL_OBJECT_LOOP( node: j: key: val); for each array 'node' to
get it's subfields.
// when 'load_subfield' is run, "key" will contain
// the JSON field name, and "val" will contain
// a YAJL node from which the value can be extracted
exsr load_subfield;
enddo;
enddo;
46
RPG Reading JSON (5 of 6)
begsr load_subfield;
select;
when key = 'invoice';
result.list(i).inv = yajl_get_string(val);
when key = 'date';
dateUSA = yajl_get_string(val);
result.list(i).date = %dec(%date(dateUSA:*usa):*iso);
when key = 'name';
result.list(i).name = yajl_get_string(val);
when key = 'amount';
result.list(i).amount = yajl_get_number(val);
when key = 'weight';
result.list(i).weight = yajl_get_number(val);
endsl;
endsr;
47
Just for the sake of having some output, here's a quick & dirty routine
to print the invoice list (with O-specs)
D prt ds likeds(list_t)
.
.
for i = 1 to YAJL_ARRAY_SIZE(list);
prt = result.list(i);
except print;
endfor;
.
.
OQSYSPRT E PRINT
O PRT.INV 5
O PRT.DATE 17 ' - - '
O PRT.NAME 44
O PRT.AMOUNT L 56
O PRT.WEIGHT L 67
48
RPG Reading JSON -- Output
49
Although there isn't time to go into detail about how to code RESTful
web services in this presentation, the gist would be:
Examples are provided in the sample code on Scott's web site, here:
http://www.scottklement.com/yajl
50
This Presentation
Thank you!
51