Homelab, Linux & ABAP (~˘▾˘)~
 

[ABAP] JSON to ABAP with dereferencing

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>).

    "...

8 Comments

  1. Sebastian

    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

  2. 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”,”(…)

    1. nocin

      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

  3. Pingback: How to post SAC API output into BW/4HANA using ABAP -

  4. Pingback: How to post SAC API output into BW/4HANA using ABAP – Erptimal Inc. / Ltd. | Certified SAP Financials Consultants

  5. Pingback: How to post SAC API output into BW/4HANA using ABAP - SharewareDepo.com - Latest Web Hosting News

  6. Pingback: How to post SAC API output into BW/4HANA using ABAP - SharewarePile.com - Latest Web Hosting News

Leave a Reply

Your email address will not be published.