
This is an alpha version yet, but I am a bit excited to have this done lately, so I decided to post it on SDN.
Business background
At my current project at work we need to integrate our web order system with SAP. Among others we use Web Services via SOAP, and do some direct HTTP POSTs from SAP. Together with my PHP specialist collegue, we decided to exchange data in JSON format. Fortunatelly for him, he has parsers to interpret JSON and build one, unfortunatelly for me we are still on SAP NW 6.4 where no buit-in JSON converter is in place. I know there are some open source projects, but for one reason or another I cannot use them properly. I decided to build mine ABAP to JSON then with OO pattern which seems to fit the requirements. JSON to ABAP will likely to born later, hopefully by utilizing same classes.
Note
I am not competing with any of open source ABAP_2_JSON tools. Those projects are most likely more consistent and tested than mine. This post is only intended to give you an insight how we can achieve that with OO approach which I found simplier and more enjoyable to develop.
JSON as a tree
While thinking of a JSON format I realized it has tree structure, i.e. following JSON string:
{
"family": {
"adults": [
{
"father": { "name": "Hommer" }
},
{
"mother": { "name": "Marge" }
}
],
"children": [
{ "name": "Lisa" },
{ "name": "Bart" },
{ "name": "Maggie", "age": "1" }
]
}
}
...can be presented in a tree-like form:
JSON to Objects
Let's break this down to objects:
Relationships
Considering above we therefore have almost all kinds of JSON combinations:
Actually we have all the objects already defined. These are:
These can be transformed into classes. Before that, however, let's have a look at well-known design pattern - COMPOSITE, which seems suitable for our case.
JSON classes with Composite pattern
A composite pattern is used to map objects' relationhips to a tree-like structure. It consists of LEAVES and nodes (COMPOSITES), all of which are tree COMPONENTS. The biggest asset of using this pattern, is that you can treat all of tree objects uniformly, no matter it is a LEAF or a COMPOSITE or a set of them. Additionally COMPOSITES serve the purpose of a container for other COMPONENTS, so we can easily add or remove different COMPONENTS to and from a tree structure.
With this in mind, after bit a tweaking we end up with such UML class diagram for our case:
Design details
Attributes
Types
Constructors
JSON SIMPLE OBJECT and JSON COMP OBJECT
JSON COMP ARRAY
Add method - it simply adds a component to components attribute
JSON SIMPLE OBJECT
JSON COMP OBJECT
JSON COMP ARRAY
Remove method
JSON SIMPLE OBJECT
JSON COMP OBJECT
JSON COMP ARRAY
Note
With such small differences in implementation of these methods and fact that JSON format is not likely to change, we could handle object and array in JSON COMPOSITE by passing object type (as one of the constants) to constructor. By using IF or CASE we could distinguish those small differences in all the methods within JSON COMPOSITE. So we might refrain from introducing new subclasses. As we don't however know which way the project might evelove (i.e. it may support to_abap functionality), it is fine to leave the design as-is.
to_json method
JSON SIMPLE OBJECT
JSON COMPOSITE OBJECT
I share the code as SAPLink nugget available for download on GitHub here. Study it or use it according to your will.
Note
You will need SAPLink plugin MSAG (message class) available for download here.
Sample usage
Following how we can use the small library by nesting different JSON COMPONENTS:
report zac_rep_to_json_example.
data: go_json_simple_obj type ref to zut_cl_json_simple_object,
go_json_comp_obj type ref to zut_cl_json_comp_object,
go_json_comp_obj2 type ref to zut_cl_json_comp_object,
go_json_comp_arr type ref to zut_cl_json_comp_array.
data: gv_json type string.
* { "adults" : [] , "children" : [] }
create object go_json_comp_obj.
* left branch
* [ {} , {} ]
create object go_json_comp_arr.
* { "name" : "Hommer" }
create object go_json_simple_obj.
go_json_simple_obj->add( key = 'name' value = 'Hommer' ).
* { "father" : { } }
create object go_json_comp_obj2.
go_json_comp_obj2->add( key = 'father' value = go_json_simple_obj ).
go_json_comp_arr->add( go_json_comp_obj2 ).
* { "name" : "Marge" }
create object go_json_simple_obj.
go_json_simple_obj->add( key = 'name' value = 'Marge' ).
* { "mother" : { } }
create object go_json_comp_obj2.
go_json_comp_obj2->add( key = 'mother' value = go_json_simple_obj ).
go_json_comp_arr->add( go_json_comp_obj2 ).
go_json_comp_obj->add( key = 'adults' value = go_json_comp_arr ).
* right branch
* [ {}, {}, {} ]
create object go_json_comp_arr.
* { "name" : "Lisa" }
create object go_json_simple_obj.
go_json_simple_obj->add( key = 'name' value = 'Lisa' ).
go_json_comp_arr->add( go_json_simple_obj ).
* { "name" : "Maggie", "age" : "1" }
create object go_json_simple_obj.
go_json_simple_obj->add( key = 'name' value = 'Maggie' ).
go_json_simple_obj->add( key = 'age' value = '1' ).
go_json_comp_arr->add( go_json_simple_obj ).
* { "name" : "Bart" }
create object go_json_simple_obj.
go_json_simple_obj->add( key = 'name' value = 'Bart' ).
go_json_comp_arr->add( go_json_simple_obj ).
go_json_comp_obj->add( key = 'children' value = go_json_comp_arr ).
* { "family" : { } }
create object go_json_comp_obj2.
go_json_comp_obj2->add( key = 'family' value = go_json_comp_obj ).
gv_json = go_json_comp_obj2->to_json( ).
write gv_json.
As gv_json is not formatted, it will be truncated when you run it, so I suggest to place a break-point and grab gv_json content from the debugger. Then you can validate it at JSONLint. Please ensure the string will not break its line at the middle of the word first prior to validation.
Note
You can manipulate JSON hierachy by removing node with remove method and by providing:
Table and structure to JSON
With our small API we can now build simple converters which accept flat structure or table on input and convert it to a JSON string. This is also added to a library.
And here is sample program for it.
report zac_rep_to_json_example2.
data: go_json type ref to zut_cl_json_component.
data gt_sflight type table of sflight.
data gv_json type string.
select * from sflight into table gt_sflight up to 2 rows.
go_json = zut_cl_json_utils=>convert_tab_to_json( gt_sflight ).
gv_json = go_json->to_json( ).
write gv_json.
Again I copy gv_json content, paste it to JSONLint, manully correct line breaks and press Validate. This gives nice formatted result:
[
{
"MANDT": "100",
"CARRID": "AA",
"CONNID": "0017",
"FLDATE": "20130403",
"PRICE": "422.94",
"CURRENCY": "USD",
"PLANETYPE": "747-400",
"SEATSMAX": "385",
"SEATSOCC": "374",
"PAYMENTSUM": "192708.71",
"SEATSMAX_B": "31",
"SEATSOCC_B": "28",
"SEATSMAX_F": "21",
"SEATSOCC_F": "21"
},
{
"MANDT": "100",
"CARRID": "AA",
"CONNID": "0017",
"FLDATE": "20130612",
"PRICE": "422.94",
"CURRENCY": "USD",
"PLANETYPE": "747-400",
"SEATSMAX": "385",
"SEATSOCC": "371",
"PAYMENTSUM": "192619.72",
"SEATSMAX_B": "31",
"SEATSOCC_B": "30",
"SEATSMAX_F": "21",
"SEATSOCC_F": "20"
}
]
Unsupported functionality
This simple library does not fully support JSON functionality. The missing parts are:
Afterword
Please don't treat this small library as a out-of-the-box API which you can use for ABAP to JSON converter. The purpose was rather to present you with an ABAP Objects solution by means of a Composite pattern. I won't release it offically unless I use it in couple of my projects, evolve it or get a positive feedback from you.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
2 | |
2 | |
2 | |
2 | |
1 | |
1 | |
1 | |
1 | |
1 | |
1 |