-
-
Save mydoghasworms/2291540 to your computer and use it in GitHub Desktop.
* Rather use https://gist.github.com/mydoghasworms/4888a832e28491c3fe47 | |
* The alternative is a better parser although it is not an emmitter) | |
*----------------------------------------------------------------------* | |
* CLASS json_util DEFINITION | |
*----------------------------------------------------------------------* | |
class json_util definition. | |
public section. | |
class-methods: | |
data_to_json importing data type any | |
returning value(json) type string, | |
json_to_data importing json type string | |
changing data type any. | |
endclass. "json_util DEFINITION | |
*----------------------------------------------------------------------* | |
* CLASS json_util IMPLEMENTATION | |
*----------------------------------------------------------------------* | |
class json_util implementation. | |
method data_to_json. | |
data: lr_desc type ref to cl_abap_typedescr. | |
data: lr_elem type ref to cl_abap_elemdescr. | |
data: lr_sdes type ref to cl_abap_structdescr. | |
data: ls_comp type cl_abap_structdescr=>component. | |
data: lt_comp type cl_abap_structdescr=>component_table. | |
data: lv_json type string. | |
data: lv_field type string. | |
data: lv_value type text255. | |
field-symbols: <field> type any. | |
data: lt_x031l type dd_x031l_table. | |
data: ls_x031l type x031l. | |
data: ls_dfies type dfies. | |
data: lv_meth(30) type c value 'GET_DDIC_FIELD'. | |
data: lv_date type d. | |
data: lv_date_c(10) type c. | |
data: lv_time type t. | |
data: lv_time_c(8) type c. | |
data: lv_tabix type i. | |
data: lv_index type i. | |
data: lv_passed1st type boole_d. | |
lr_desc = cl_abap_typedescr=>describe_by_data( data ). | |
case lr_desc->type_kind. | |
when cl_abap_typedescr=>typekind_struct1 or cl_abap_typedescr=>typekind_struct2. | |
json = '{'. | |
* Use RTTI to discover structure members and process them individually | |
lr_sdes ?= lr_desc. | |
lt_comp = lr_sdes->get_components( ). | |
loop at lt_comp into ls_comp. | |
assign component ls_comp-name of structure data | |
to <field>. | |
if sy-subrc = 0 and <field> is not initial. | |
* For consecutive elements, add a comma separator after the previous value | |
if lv_passed1st = 'X'. | |
concatenate json ',' into json. | |
endif. | |
lv_json = data_to_json( data = <field> ). | |
concatenate json ' "' ls_comp-name '": ' lv_json into json. | |
lv_passed1st = 'X'. | |
endif. | |
endloop. | |
concatenate json '}' into json. | |
when cl_abap_typedescr=>typekind_table. | |
data: ld_line type ref to data. | |
field-symbols: <tab> type any table. | |
field-symbols: <line> type any. | |
assign data to <tab>. | |
create data ld_line like line of <tab>. | |
assign ld_line->* to <line>. | |
* Open array for table entries | |
json = '['. | |
loop at <tab> into <line>. | |
lv_json = data_to_json( data = <line> ). | |
concatenate json lv_json into json separated by space. | |
at last. | |
continue. | |
endat. | |
* Separate consecutive values by commas | |
concatenate json ',' into json. | |
endloop. | |
* Close array for table entries | |
concatenate json ']' into json separated by space. | |
when cl_abap_typedescr=>typekind_dref. | |
* For data references, dereference the data and call method again | |
field-symbols: <data> type any. | |
assign data->* to <data>. | |
if sy-subrc = 0. | |
json = data_to_json( data = <data> ). | |
else. | |
json = '{}'. "Will produce empty JS object | |
endif. | |
when others. | |
* For elementary types, we merely return a text representation of the value | |
json = data. | |
condense json. | |
* Escape special characters | |
replace all occurrences of '\' in json with '\\'. | |
replace all occurrences of '"' in json with '\"'. | |
replace all occurrences of cl_abap_char_utilities=>newline in json with '\n'. | |
replace all occurrences of cl_abap_char_utilities=>horizontal_tab in json with '\t'. | |
replace all occurrences of cl_abap_char_utilities=>form_feed in json with '\f'. | |
replace all occurrences of cl_abap_char_utilities=>vertical_tab in json with '\v'. | |
replace all occurrences of cl_abap_char_utilities=>backspace in json with '\b'. | |
* Numeric values do not need to be escaped | |
if lr_desc->type_kind ne cl_abap_typedescr=>typekind_num and | |
lr_desc->type_kind ne cl_abap_typedescr=>typekind_packed and | |
lr_desc->type_kind ne cl_abap_typedescr=>typekind_float and | |
lr_desc->type_kind ne cl_abap_typedescr=>typekind_int and | |
lr_desc->type_kind ne cl_abap_typedescr=>typekind_int1 and | |
lr_desc->type_kind ne cl_abap_typedescr=>typekind_int2. | |
concatenate '"' json '"' into json. | |
endif. | |
endcase. | |
endmethod. "data_to_json | |
method json_to_data. | |
data: lv_off type i. | |
data: lv_len type i. | |
data: lv_key type string. | |
data: lv_value type string. | |
data: lv_char type char1. "Current chacater | |
data: lv_pchar type char1. "Previous character | |
data: lv_instr type boole_d. "Indicator: cursor in string | |
data: lv_level type i. "Depth inside a block | |
data: lv_table type boole_d. | |
field-symbols: <fk> type string. | |
field-symbols: <data> type any. | |
field-symbols: <table> type any table. | |
field-symbols: <sotab> type sorted table. | |
field-symbols: <sttab> type standard table. | |
field-symbols: <line> type any. | |
data: ls_line type ref to data. | |
data: lr_td type ref to cl_abap_typedescr. | |
data: lr_ttd type ref to cl_abap_tabledescr. | |
* If the incoming json contains no '{}[]', we are dealing with | |
* a basic (scalar) value that is simply assigned to the data | |
* and then we return | |
if json na '{}[]'. | |
* Replace escape characters (TODO: Check if there are more!) | |
lv_value = json. | |
replace all occurrences of '\n' in lv_value with cl_abap_char_utilities=>newline. | |
replace all occurrences of '\t' in lv_value with cl_abap_char_utilities=>horizontal_tab. | |
replace all occurrences of '\f' in lv_value with cl_abap_char_utilities=>form_feed. | |
replace all occurrences of '\v' in lv_value with cl_abap_char_utilities=>vertical_tab. | |
replace all occurrences of '\b' in lv_value with cl_abap_char_utilities=>backspace. | |
replace all occurrences of '\\' in lv_value with '\'. | |
* TODO: Deal with specific data types, e.g. dates etc. | |
data = lv_value. | |
exit. | |
endif. | |
lv_len = strlen( json ). | |
* Check if we are dealing with a table | |
lr_td = cl_abap_typedescr=>describe_by_data( data ). | |
if lr_td->type_kind = cl_abap_typedescr=>typekind_table. | |
* This information is used later... | |
lv_table = 'X'. | |
assign data to <table>. | |
create data ls_line like line of <table>. | |
assign ls_line->* to <line>. | |
else. | |
lv_table = ' '. | |
endif. | |
* Reset counters/flags | |
lv_off = 0. | |
lv_instr = ' '. | |
lv_level = 0. | |
assign lv_key to <fk>. | |
while lv_off < lv_len. | |
lv_char = json+lv_off(1). | |
********************************************************************** | |
* IN STRING | |
********************************************************************** | |
* Character is in a string delimited by double quotes | |
if lv_instr = 'X'. | |
if lv_char = '"'. | |
* Switch out of delimited character string | |
if lv_pchar ne '\'. | |
lv_instr = ' '. | |
else. | |
concatenate <fk> lv_char into <fk> respecting blanks. | |
endif. | |
else. | |
concatenate <fk> lv_char into <fk> respecting blanks. | |
endif. | |
********************************************************************** | |
* OUTSIDE STRING | |
********************************************************************** | |
* Character is not in a string delimited by double quotes | |
else. | |
* On opening character, shift level up | |
if lv_char ca '{['. | |
add 1 to lv_level. | |
endif. | |
* When the value is contained in a {}/[], the entire value must | |
* be passed to the next level of processing | |
if lv_level > 1. | |
concatenate <fk> lv_char into <fk> respecting blanks. | |
else. | |
if lv_char ca '[{'. "<- Chars ignored outside of str | |
elseif lv_char = ':'. | |
assign lv_value to <fk>. | |
* The key collected up to now is assigned to the data member | |
translate lv_key to upper case. | |
shift lv_key left deleting leading space. | |
assign component lv_key of structure data to <data>. | |
* End of a key/value pair (we bump up against delimiter) - pass to next level | |
elseif ( lv_char = ',' or lv_char = '}' ) and lv_table = space. | |
* Process collected value | |
shift lv_value left deleting leading space. | |
json_to_data( exporting json = lv_value changing data = <data> ). | |
* Clear key and value | |
clear: lv_key, lv_value. | |
assign lv_key to <fk>. | |
clear: lv_key, lv_value. | |
* End of a key/value pair (we bump up against delimiter) - pass to next level | |
* But in table mode, we pass an instance of a row of the table, and afterward | |
* add it to the table | |
elseif ( lv_char = ',' or lv_char = ']' ) and lv_table = 'X'. | |
* Process collected value | |
* Inside array in JSON, there are no keys, only list of values, the collected | |
* value is in lv_key | |
shift lv_key left deleting leading space. | |
json_to_data( exporting json = lv_key changing data = <line> ). | |
* On return: if dealing with table, add the record to the table | |
lr_ttd ?= lr_td. | |
if lr_ttd->table_kind = cl_abap_tabledescr=>tablekind_sorted | |
or lr_ttd->table_kind = cl_abap_tabledescr=>tablekind_hashed. | |
assign data to <sotab>. | |
insert <line> into table <sotab>. | |
else. | |
assign data to <sttab>. | |
append <line> to <sttab>. | |
endif. | |
clear <line>. | |
* Clear key and value | |
clear: lv_key, lv_value. | |
assign lv_key to <fk>. | |
clear: lv_key, lv_value. | |
* Switch cursor into delimited string; consecutive characters | |
* are then treated as part of a key or value, even if they are | |
* special characters | |
elseif lv_char = '"'. | |
lv_instr = 'X'. | |
* Other chars processed as either key or value | |
else. | |
concatenate <fk> lv_char into <fk> respecting blanks. | |
endif. | |
endif. | |
* On closing character, shift level down again | |
if lv_char ca '}]'. | |
subtract 1 from lv_level. | |
endif. | |
* END: Are we in string or out? | |
endif. | |
lv_pchar = lv_char. | |
add 1 to lv_off. | |
endwhile. | |
endmethod. "json_to_data | |
endclass. "json_util IMPLEMENTATION |
Hi,
if a structure includes another structure you need to insert code between line 48 and 49 to extract the components of each structure recursively and make sure that gt_comp has all fields like SE11 expands the .INCLUDE structures.
also if a numeric value is processed and has leading zeros I would suggest to call this FM like here (line #116):
() Numeric values do not need to be escaped
IF lo_desc->type_kind NE cl_abap_typedescr=>typekind_num AND
lo_desc->type_kind NE cl_abap_typedescr=>typekind_packed AND
lo_desc->type_kind NE cl_abap_typedescr=>typekind_float AND
lo_desc->type_kind NE cl_abap_typedescr=>typekind_int AND
lo_desc->type_kind NE cl_abap_typedescr=>typekind_int1 AND
lo_desc->type_kind NE cl_abap_typedescr=>typekind_int2.
CONCATENATE '"' rv_json '"' INTO rv_json.
ELSE.
() Eliminate leading zeros for numeric values
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_OUTPUT'
EXPORTING
input = rv_json
IMPORTING
output = rv_json.
ENDIF.
Cheers,
Martin
hey please help me out i am new in abap .
How to get field from json format webervice into abap code.
hi i need to consume the rest web service through abap program... I have all the details for the WSDL URL or rest web service which is created in cake PHP .Now i need to send some data in structure through our abap code ... i stuck in this process please let me know the solution.. i will be very grateful to you guys ...thanks pallavi
Greetings,
line 153 : if json na '{}[]'.
Completely fails when we are expecting a string , and that string contains any of those characters.
I think you will also need to check the value of p_abap ( DESCRIBE ), and if it is a string or char, then regardless of those characters, you are dealing with a scalar.
Other than, I have used your code for close to a year, it never broke till now.