04-06-2020 7:27 PM
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.
04-07-2020 3:49 AM
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
).
04-07-2020 3:49 AM
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
).
04-07-2020 7:28 AM
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
04-07-2020 8:13 PM
09-16-2020 4:30 PM
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
05-11-2022 9:00 AM