Recently I had to fetch an attachment from SuccessFactors. The API returns a huge JSON. Here only a few lines of that json.
{
"d": {
"__metadata": {
"uri": "https://apihostname.successfactors.com:443/odata/v2/Attachment(1000)",
"type": "SFOData.Attachment"
},
"attachmentId": "1000",
"fileName": "Testdokument.pdf",
"mimeType": "application/pdf",
"moduleCategory": "HRIS_ATTACHMENT",
"userId": "10000555",
"fileExtension": "pdf",
"fileContent": "JVBERi0xLjcKjp2jtMXW5/gKMiAwIG9iag0KWy9JQ0NCYXNlZCAzIDAgUl0NCmVuZG9iag0KMyAw\r\nIG9iag0KPDwNCi9GaWx0ZXIgL0ZsYXRlRGVjb2RlIA0KL0xlbmd0aCAyNTk2IA0KL04gMyANCj4+\r\nDQpzdHJlYW0NCnicnZZ3VFPZFofPvTe9UJIQipTQa2hSAkgNvUiRLioxCRBKwJAAIjZEVHBEUZGm\r\nCDIo4ICjQ5GxIoqFAVGx6wQZRNRxcBQblklkrRnfvHnvzZvfH/d+a5+9z91n733WugCQ/IMFwkxY\r\nCYAMoVgU4efFiI2LZ2AHAQzwAANsAOBws7NCFvhGApkCfNiMbJkT+Be9ug4g+fsq0z+MwQD/n5S5\r\nWSIxAFCYjOfy+NlcGRfJOD1XnCW3T8mYtjRNzjBKziJZgjJWk3PyLFt89pllDznzMoQ8GctzzuJl\r\n8OTcJ+ONORK+jJFgGRfnCPi5Mr4mY4N0SYZAxm/ksRl8TjYAKJLcLuZzU2RsLWOSKDKCLeN5AOB......"
...
}
}
Parsing the JSON to ABAP
SAP provides two classes /ui2/cl_json
and /ui2/cl_data_access
to work with JSONs and there are also many cool tools on https://dotabap.org/ for that.
Because it’s just a simple JSON I tried to avoid importing another class to the system. So I stayed with SAPs classes.
First deserialize the JSON with /ui2/cl_json
.
" Constants for properties
CONSTANTS gc_filecontent TYPE string VALUE 'FILECONTENT' ##NO_TEXT.
CONSTANTS gc_filename TYPE string VALUE 'FILENAME' ##NO_TEXT.
CONSTANTS gc_doc_cat TYPE string VALUE 'DOCUMENTCATEGORY' ##NO_TEXT.
DATA: lr_data TYPE REF TO data.
/ui2/cl_json=>deserialize( EXPORTING json = lv_responce "the repsonse contains the json string
CHANGING data = lr_data ).
I found three similiar ways to use the class /ui2/cl_data_access
to get the relevant data out of the JSON.
1. Using the class construktor and the value( )
method for earch property.
DATA: lv_filecontent_base64 TYPE string,
lv_filename TYPE string,
lv_documentcategory TYPE string.
/ui2/cl_data_access=>create( ir_data = lr_data iv_component = |D-{ gc_filecontent }| )->value( IMPORTING ev_data = lv_filecontent_base64 ).
/ui2/cl_data_access=>create( ir_data = lr_data iv_component = |D-{ gc_filename }| )->value( IMPORTING ev_data = lv_filename).
/ui2/cl_data_access=>create( ir_data = lr_data iv_component = |D-{ gc_doc_cat }| )->value( IMPORTING ev_data = lv_documentcategory ).
2. Create an instance as first step and pass the component. Reuse the instance with the value( )
method.
DATA: lv_filecontent_base64 TYPE string,
lv_filename TYPE string,
lv_documentcategory TYPE string.
DATA(lo_data_access) = /ui2/cl_data_access=>create( ir_data = lr_data
iv_component = |D-| ).
lo_data_access->at( |{ gc_filecontent }| )->value( IMPORTING ev_data = lv_filecontent_base64 ).
lo_data_access->at( |{ gc_filename }| )->value( IMPORTING ev_data = lv_filename ).
lo_data_access->at( |{ gc_doc_cat }| )->value( IMPORTING ev_data = lv_documentcategory ).
3. Use the ref( )
method to get a reference of a property value. Because it returns a generic data type (and you cannot dereference generic references) you have to use CAST
combined with dereferencing (->*
) to get the concret value.
DATA(lo_data_access) = /ui2/cl_data_access=>create( ir_data = lr_data
iv_component = |D-| ).
DATA(lv_filecontent_base64) = CAST string( lo_data_access->at( gc_filecontent )->ref( ) )->*.
DATA(lv_filename) = CAST string( lo_data_access->at( gc_filename )->ref( ) )->*.
DATA(lv_documentcategory) = CAST string( lo_data_access->at( gc_doc_cat )->ref( ) )->*.
In the end I stayed with version 3 because I prefere using inline declarations. The complete result looked like this:
" Constants for properties
CONSTANTS gc_filecontent TYPE string VALUE 'FILECONTENT' ##NO_TEXT.
CONSTANTS gc_filename TYPE string VALUE 'FILENAME' ##NO_TEXT.
CONSTANTS gc_doc_cat TYPE string VALUE 'DOCUMENTCATEGORY' ##NO_TEXT.
DATA: lr_data TYPE REF TO data.
/ui2/cl_json=>deserialize( EXPORTING json = lv_response "the repsonse contains the json string
CHANGING data = lr_data ).
DATA(lo_data_access) = /ui2/cl_data_access=>create( ir_data = lr_data
iv_component = |D-| ).
DATA(lv_filecontent_base64) = CAST string( lo_data_access->at( gc_filecontent )->ref( ) )->*.
DATA(lv_filename) = CAST string( lo_data_access->at( gc_filename )->ref( ) )->*.
DATA(lv_documentcategory) = CAST string( lo_data_access->at( gc_doc_cat )->ref( ) )->*.
" Decode BASE64 PDF to XString
DATA(lv_decodedx) = cl_http_utility=>if_http_utility~decode_x_base64( lv_filecontent_base64 ).
" XString to binary
DATA(lt_data) = cl_bcs_convert=>xstring_to_solix( lv_decodedx ).
" Download file
cl_gui_frontend_services=>gui_download( EXPORTING bin_filesize = xstrlen( lv_decodedx )
filename = lv_filename
filetype = 'BIN'
show_transfer_status = ' '
CHANGING data_tab = lt_data ).
When /ui2/cl_data_access
is not available (SAP_UI has to be at least 7.51, see note 2526405), you can do it the oldschool way.
" Constants for properties
CONSTANTS gc_filecontent TYPE string VALUE 'FILECONTENT' ##NO_TEXT.
CONSTANTS gc_filename TYPE string VALUE 'FILENAME' ##NO_TEXT.
CONSTANTS gc_doc_cat TYPE string VALUE 'DOCUMENTCATEGORY' ##NO_TEXT.
DATA: lr_data TYPE REF TO data.
/ui2/cl_json=>deserialize( EXPORTING json = lv_response "the repsonse contains the json string
CHANGING data = lr_data ).
ASSIGN lr_data->* TO FIELD-SYMBOL(<fs_d>).
ASSIGN COMPONENT 'D' OF STRUCTURE <fs_d> TO FIELD-SYMBOL(<fs_components>).
ASSIGN <fs_components>->* TO FIELD-SYMBOL(<fs_fields>).
ASSIGN COMPONENT gc_doc_cat OF STRUCTURE <fs_fields> TO FIELD-SYMBOL(<documentcategory>).
ASSIGN COMPONENT gc_filename OF STRUCTURE <fs_fields> TO FIELD-SYMBOL(<filename>).
ASSIGN COMPONENT gc_filecontent OF STRUCTURE <fs_fields> TO FIELD-SYMBOL(<filecontent>).
ASSIGN <documentcategory>->* TO FIELD-SYMBOL(<documentcategory_value>).
ASSIGN <filename>->* TO FIELD-SYMBOL(<filename_value>).
ASSIGN <filecontent>->* TO FIELD-SYMBOL(<filecontent_value>).
"...
Hi Nocin,
Thank you for this revolutionary ABAP content 😉
Could you please tell me what could potentially go wrong ASSIGN COMPONENT ‘D’? What does the ‘D’ actually mean?
I tried all the methods and all of them do not produce any content in respective content.
I have a standard table in lr_data exported by /ui2/cl_json=>deserialize but no matter what I try, I cannot move it from structure to field symbol. The field symbol always remains empty.
” Has filled standard table
ASSIGN lr_data->* TO FIELD-SYMBOL().
” is empty / initial
ASSIGN COMPONENT ‘D’ OF STRUCTURE TO FIELD-SYMBOL().
Thanks,
Sebastian
I would like to correct my question 🙂
What if the JSON does not have a header element such as ‘D’, should I concatenate it into response?
My JSON starts as below:
[{“name”:”Filter Dashboard”,”(…)
Hi Sebastian,
the elelemt ‘D’ comes from my JSON file and because its a nested JSON, I always hat to go two layers deep to get to the data.
But as you already found out, you can skip this step as your JSON has its properties on the first level. Or at least, you have an array on the first level which contains the objects. 🙂
BR
Nico
Sorry about the spam, I was able to make it work with concatenation and the ‘oldschool’ method 🙂
Pingback: How to post SAC API output into BW/4HANA using ABAP -
Pingback: How to post SAC API output into BW/4HANA using ABAP – Erptimal Inc. / Ltd. | Certified SAP Financials Consultants
Pingback: How to post SAC API output into BW/4HANA using ABAP - SharewareDepo.com - Latest Web Hosting News
Pingback: How to post SAC API output into BW/4HANA using ABAP - SharewarePile.com - Latest Web Hosting News