ABAP and JSON
JSON format is widely used these days. While developing using SAP ABAP, we may need to interpret, read or create JSON formatted data. In this blog post, I will try to explain how we can do the operations we need.
To give some information about JSON (JavaScript Object Notation) before starting to use ABAP:
- It is based on a subset of the JavaScript programming language standard.
- It is easy for programmers to read and write.
- It is easy for machines to parse and create.
- It is based on two structures:
- A collection of name-value pairs
- An ordered list of values
JSON Examples
The first example has a collection of name and value pairs. These pairs are shown in the following structure, which includes material (matnr), material definition (maktx), basic unit of measurement (meins) and values.
{ "matnr": "50065557", "maktx": "Test Malzeme", "meins": "ST" }
The second example below shows the list of collections in the first example.
{ "materials": [ { "matnr": "50065557", "maktx": "Test Malzeme", "meins": "ST" }, { "matnr": "50065558", "maktx": "Test Malzeme2", "meins": "ST" }, { "matnr": "50065559", "maktx": "Test Malzeme3", "meins": "ST" } ] }
JSON ve XML
JSON works similarly to XML. Both are programmer readable, both are hierarchical and used in various programming languages. However, JSON is simpler than XML, does not use tags and is therefore shorter and easier to read and write.
Now let's go back to how JSON can be processed in ABAP.
Validating JSON Data in ABAP
We will check the accuracy of the data using the CL_SXML_STRING_READER class. As I mentioned above, JSON and XML are similar, so the same class can be used for both.
DATA lv_json TYPE string VALUE '{"matnr":"50065557","maktx":"Test Malzeme","meins":"ST"}'. DATA(lo_reader) = cl_sxml_string_reader=>create( cl_abap_codepage=>convert_to( lv_json ) ). TRY. lo_reader->next_node( ). lo_reader->skip_node( ). cl_demo_output=>display( 'JSON is valid' ). CATCH cx_sxml_parse_error INTO DATA(lx_parse_error). cl_demo_output=>display( lx_parse_error->get_text( ) ). ENDTRY.
When JSON is valid, we output "JSON is valid".
Next, let's change our JSON string to get an error and try again.
DATA lv_json TYPE string VALUE '{"matnr":"50065557","maktx":"Test Malzeme","meins":"ST"ASL}'. DATA(lo_reader) = cl_sxml_string_reader=>create( cl_abap_codepage=>convert_to( lv_json ) ). TRY. lo_reader->next_node( ). lo_reader->skip_node( ). cl_demo_output=>display( 'JSON is valid' ). CATCH cx_sxml_parse_error INTO DATA(lx_parse_error). cl_demo_output=>display( lx_parse_error->get_text( ) ). ENDTRY.
When we execute the program, “Error while parsing an XML stream: '',' or '}' expected at 'ASL}''.” We get the output.
Reading JSON Data in ABAP
We will read and parse the data using the /UI2/CL_JSON class. We have different alternatives to do this, but it is a good option for a start.
TYPES: BEGIN OF ty_material, matnr TYPE string, maktx TYPE string, meins TYPE string, END OF ty_material. DATA: lv_json TYPE string, lr_data TYPE REF TO data, ls_material TYPE ty_material, lt_material TYPE STANDARD TABLE OF ty_material. FIELD-SYMBOLS TYPE ANY TABLE. lv_json = '{"materials": [{"matnr":"50065557","maktx":"Test Malzeme","meins":"ST"},' && '{"matnr":"50065558","maktx":"Test Malzeme2","meins":"ST"},' && '{"matnr":"50065559","maktx":"Test Malzeme3","meins":"ST"} ] } '. /ui2/cl_json=>deserialize( EXPORTING json = lv_json pretty_name = /ui2/cl_json=>pretty_mode-user assoc_arrays = abap_true CHANGING data = lr_data ). IF lr_data IS BOUND. ASSIGN lr_data->* TO FIELD-SYMBOL(). ASSIGN COMPONENT 'MATERIALS' OF STRUCTURE TO FIELD-SYMBOL(). ASSIGN ->* TO . LOOP AT ASSIGNING FIELD-SYMBOL(). DO 3 TIMES. CASE sy-index. WHEN 1. DATA(lv_fname) = 'MATNR'. WHEN 2. lv_fname = 'MAKTX'. WHEN 3. lv_fname = 'MEINS'. ENDCASE. ASSIGN COMPONENT sy-index OF STRUCTURE ls_material TO FIELD-SYMBOL(). ASSIGN ->* TO FIELD-SYMBOL(). ASSIGN COMPONENT lv_fname OF STRUCTURE TO FIELD-SYMBOL(). IF IS ASSIGNED AND IS ASSIGNED. ASSIGN ->* TO FIELD-SYMBOL(). IF IS ASSIGNED. = . ENDIF. ENDIF. ENDDO. APPEND ls_material TO lt_material. ENDLOOP. cl_demo_output=>display( lt_material ). ENDIF.
When we run the program, the output will be as follows.
Generating JSON Data in ABAP
SELECT * FROM scarr INTO TABLE @DATA(lt_scarr). DATA(lv_json) = /ui2/cl_json=>serialize( data = lt_scarr compress = abap_true pretty_name = /ui2/cl_json=>pretty_mode-camel_case ). " Display JSON in ABAP CALL TRANSFORMATION sjson2html SOURCE XML lv_json RESULT XML DATA(lvc_html). cl_abap_browser=>show_html( title = 'Sample JSON' html_string = cl_abap_codepage=>convert_from( lvc_html ) ).
The output of the program will be as follows.
In the above example, we used CALL TRANSFORMATION to display JSON as HTML. Here we will use it as internal table -> JSON conversion.
DATA lv_json TYPE string. SELECT * FROM scarr INTO TABLE @DATA(lt_scarr). DATA(lo_writer) = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ). CALL TRANSFORMATION id SOURCE values = lt_scarr RESULT XML lo_writer. cl_abap_conv_in_ce=>create( )->convert( EXPORTING input = lo_writer->get_output( ) IMPORTING data = lv_json ).
The JSON string looks like below in debug.
We can still convert the string we created via CALL TRANSFORMATION.
CLEAR lt_scarr. CALL TRANSFORMATION id SOURCE XML lv_json RESULT values = lt_scarr. cl_demo_output=>display( lt_scarr ).
The output of the program will be as follows.
You can search for the CL_SXML_TABLE_READER class and how to use it to get more information. You can also examine demo programs starting with DEMO_JSON_* in the SABAPDEMOS package.