Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamically create internal table from JSON using /UI2/CL_JSON and more

rllabresp
Discoverer
0 Kudos

Hi all

I have the need to call an API which returns a JSON structure which may change (add or remove fields) although the fields I need will always be there -not sure the position- but there.

I've just discovered class /UI2/CL_JSON and it works great to get the JSON and prettify it, but then I've not been able to create an internal table dinamically to then access the data.

I've tried all I know about RTTS and the cl_abap_xxdescr classes without success. I can move the data to a field-symbol and see it, but then I cannot do anything else that going around the CL_ABAP_REFDESCR class, and so I cannot read the structure.

Anyone can help me?

Thanks!!!!!

This is the JSON structure

{
  "d": {
    "results": [
      {
        "RebateNo": "1234567890",
        "ValidFromDate": "/Date(1427839200000)/",
        "ValidToDate": "/Date(1459375200000)/",
        "Type": "ZZZZ",
        "Status": "Z",
        "SalesGroup": "Z",
        "Owner": "JOHN SMITH",
        "CustomerNo": "12345",
        "CustomerName": "JOHN SMITH",
        "CustomerVAT": "12345"
        }
      },
	.....
}

This is the code I have so far.


TRY.
    CALL TRANSFORMATION sjson2html SOURCE XML response
                                   RESULT XML DATA(formatted_json).

  CATCH cx_xslt_runtime_error.
    cl_abap_browser=>show_html(
      EXPORTING
        title       = 'Something went wrong - <ESC> to continue'
        html_string = response ).
ENDTRY.

IF formatted_json IS NOT INITIAL.

  DATA: temp TYPE REF TO data.
  FIELD-SYMBOLS: <fsdata> type data.

  CALL METHOD /ui2/cl_json=>generate
    EXPORTING
      json        = response
      pretty_name = /ui2/cl_json=>pretty_mode-camel_case
*     name_mappings =
    RECEIVING
      rr_data     = temp.

  DATA(lo_data) = /ui2/cl_data_access=>create( ir_data = temp iv_component = 'd-results' )->ref( ).

  IF lo_data IS BOUND.
    ASSIGN lo_data->* TO <fsdata>.

    data lo_desc TYPE REF TO CL_ABAP_TYPEDESCR.

    lo_desc =  cl_abap_refdescr=>describe_by_data( p_data =  lo_data ).

  endif.

    cl_abap_browser=>show_html(
      EXPORTING
        title       = 'JSON File - <ESC> to continue'
        html_string = cl_abap_codepage=>convert_from( formatted_json ) ).

ENDIF.

This is what I see in the field symbol: table and deep structure, but without name so I cannot access it.

1 ACCEPTED SOLUTION

esti1
Participant

You don't need to define the internal table dynamically. If you can be sure that the fields you need are in the structure, just pre-define the fields that you are interested in. The JSON parser will ignore fields that are not relevant:

REPORT ytest_json.

"Define the structure with the fields you are interested in:
TYPES:
  BEGIN OF ty_result,
    RebateNo TYPE string,
    Type     TYPE string,
  END OF ty_result,

  BEGIN OF ty_data,
    BEGIN OF d,
      results TYPE STANDARD TABLE OF ty_result WITH EMPTY KEY,
    END OF d,
  END OF ty_data.

  DATA: lt_data TYPE ty_data.

END-OF-SELECTION.
"Messy hard-coded JSON string with some missing fields due to the 255 char limit
    DATA(lv_json_string) = `{"d":{"results":[{"RebateNo":"1234567890","Type":"ZZZZ","Status":"Z","SalesGroup":"Z","Owner":"JOHN SMITH","CustomerNo":"12345","CustomerName":"JOHN SMITH","CustomerVAT":"12345"}]}}`.

"...and deserialize your JSON
   /ui2/cl_json=>deserialize(
     EXPORTING
        json             = lv_json_string
*       jsonx            =
        pretty_name      = /ui2/cl_json=>pretty_mode-camel_case
*       assoc_arrays     =
*       assoc_arrays_opt =
*       name_mappings    =
*       conversion_exits =
     CHANGING
       data             = lt_data
   ).
5 REPLIES 5

esti1
Participant

You don't need to define the internal table dynamically. If you can be sure that the fields you need are in the structure, just pre-define the fields that you are interested in. The JSON parser will ignore fields that are not relevant:

REPORT ytest_json.

"Define the structure with the fields you are interested in:
TYPES:
  BEGIN OF ty_result,
    RebateNo TYPE string,
    Type     TYPE string,
  END OF ty_result,

  BEGIN OF ty_data,
    BEGIN OF d,
      results TYPE STANDARD TABLE OF ty_result WITH EMPTY KEY,
    END OF d,
  END OF ty_data.

  DATA: lt_data TYPE ty_data.

END-OF-SELECTION.
"Messy hard-coded JSON string with some missing fields due to the 255 char limit
    DATA(lv_json_string) = `{"d":{"results":[{"RebateNo":"1234567890","Type":"ZZZZ","Status":"Z","SalesGroup":"Z","Owner":"JOHN SMITH","CustomerNo":"12345","CustomerName":"JOHN SMITH","CustomerVAT":"12345"}]}}`.

"...and deserialize your JSON
   /ui2/cl_json=>deserialize(
     EXPORTING
        json             = lv_json_string
*       jsonx            =
        pretty_name      = /ui2/cl_json=>pretty_mode-camel_case
*       assoc_arrays     =
*       assoc_arrays_opt =
*       name_mappings    =
*       conversion_exits =
     CHANGING
       data             = lt_data
   ).

Hi,

thanks, this worked for me!

However, I'm still curious about doing this dynamically. I'll keep investigating, but any idea is more than welcome.

Thank you so much, esti

Sandra_Rossi
Active Contributor
0 Kudos
rllabresp Please open a new question if you want help for RTTS/read <FSDATA> (currently, I don't even understand what you're trying to achieve, because reading <FSDATA> doesn't require CL_ABAP_REFDESCR at all).

Gunter
Product and Topic Expert
Product and Topic Expert

Hi Rafael,

I came across your question as I had a very similar issue. I moved the code into a report shape so you can run it easily, The way I solved it dynamically is like that:

REPORT zjson2abap.

DATA:
  lv_json_body     TYPE string,
  lmsg_data        TYPE REF TO data,
  lo_pos_struct    TYPE REF TO cl_abap_structdescr.

FIELD-SYMBOLS:
  <l_msg_data>   TYPE any,
  <l_postab_ref> TYPE any,
  <lt_postab>    TYPE ANY TABLE,
  <l_pos>        TYPE any,
  <l_fvalue>     TYPE any.


lv_json_body = '{  "tenantId": "949043908", "capabilityId": "da21a3ad-6050-47b5-b1be-daf721c8748b", "sensorId": "feef4fff-0731-462e-9972-ee92923de3fd", "timestamp": 1600244897484, "gatewayTimestamp": 1600244897484,  ' &&
               '"measures": [  {  "tagId": "A32F", "zC": 1000.132, "yC": 1000.132, "xC": 324.12, "quality": 92 } ] }'.

" Generate dynamic structure to suit the data
lmsg_data = /ui2/cl_json=>generate( json = lv_json_body ).
ASSIGN lmsg_data->* TO <l_msg_data>.

" Deserialize the JSON string into the matching deep datatype
/ui2/cl_json=>deserialize( EXPORTING json = lv_json_body pretty_name = /ui2/cl_json=>pretty_mode-camel_case CHANGING data = <l_msg_data> ).

" Save the position data in resource table
ASSIGN COMPONENT 'MEASURES' OF STRUCTURE <l_msg_data> TO <l_postab_ref>.
ASSIGN <l_postab_ref>->* TO <lt_postab>.

" Loop over the table
LOOP AT <lt_postab> ASSIGNING FIELD-SYMBOL(<l_posref>).
  ASSIGN <l_posref>->* TO <l_pos>.
  lo_pos_struct ?= cl_abap_typedescr=>describe_by_data( <l_pos> ).
  DATA(lt_pos_comp) = lo_pos_struct->get_components( ).
  DO.
    ASSIGN COMPONENT sy-index OF STRUCTURE <l_pos> TO FIELD-SYMBOL(<l_field_ref>).
    IF sy-subrc <> 0. EXIT. ENDIF.
    ASSIGN <l_field_ref>->* TO <l_fvalue>.
    WRITE: / |Field: { lt_pos_comp[ sy-index ]-name } Value: { <l_fvalue> }|.
  ENDDO.
ENDLOOP.

Report output should look like this:

Field: QUALITY Value: 92
Field: TAG_ID Value: A32F
Field: X_C Value: 324.12
Field: Y_C Value: 1000.1319999999999
Field: Z_C Value: 1000.1319999999999

I believe it won't win a beauty contest but it runs completely dynamic. Trick is that every level in the generated structure has a reference level in between and needs dereferencing. Hope it helps!

Gunter

Thanks for sharing 🙂