=> Reference
- 如果系统缺少类/UI2/CL_JSON,则报错:2500033 - Type /UI2/CL_JSON is unknown
- 系统缺少类/UI2/CL_JSON,可自行实现ZCL_JSON来使用;参考文章:One more ABAP to JSON Serializer and Deserializer

- ZCL_JSON源码
*----------------------------------------------------------------------* * CLASS zcl_json DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS zcl_json DEFINITION. PUBLIC SECTION. TYPE-POOLS abap . CLASS cl_abap_tstmp DEFINITION LOAD . CLASS cx_sy_conversion_error DEFINITION LOAD . TYPES: json TYPE string, BEGIN OF name_mapping, abap TYPE abap_compname, json TYPE string, END OF name_mapping, name_mappings TYPE HASHED TABLE OF name_mapping WITH UNIQUE KEY abap, ref_tab TYPE STANDARD TABLE OF REF TO data WITH DEFAULT KEY, bool TYPE char1, tribool TYPE char1 , pretty_name_mode TYPE char1 . CONSTANTS: BEGIN OF pretty_mode, none TYPE char1 VALUE ``, low_case TYPE char1 VALUE `L`, camel_case TYPE char1 VALUE `X`, extended TYPE char1 VALUE `Y`, user TYPE char1 VALUE `U`, user_low_case TYPE char1 VALUE `C`, END OF pretty_mode, BEGIN OF c_bool, true TYPE bool VALUE `X`, false TYPE bool VALUE ``, END OF c_bool , BEGIN OF c_tribool, true TYPE tribool VALUE c_bool-true, false TYPE tribool VALUE `-`, undefined TYPE tribool VALUE ``, END OF c_tribool, mc_key_separator TYPE string VALUE `-`, "#EC NOTEXT version TYPE i VALUE 16. CLASS-DATA sv_white_space TYPE string READ-ONLY . CLASS-DATA mc_bool_types TYPE string READ-ONLY VALUE `\TYPE-POOL=ABAP\TYPE=ABAP_BOOL\TYPE=BOOLEAN\TYPE=BOOLE_D\TYPE=XFELD`. "#EC NOTEXT CLASS-DATA mc_bool_3state TYPE string READ-ONLY VALUE `\TYPE=BOOLEAN`. "#EC NOTEXT CLASS-DATA mc_json_type TYPE string READ-ONLY . CLASS-METHODS class_constructor . CLASS-METHODS string_to_xstring IMPORTING in TYPE string CHANGING VALUE(out) TYPE any . CLASS-METHODS xstring_to_string IMPORTING in TYPE any RETURNING VALUE(out) TYPE string . CLASS-METHODS raw_to_string IMPORTING iv_xstring TYPE xstring iv_encoding TYPE abap_encoding OPTIONAL RETURNING VALUE(rv_string) TYPE string . CLASS-METHODS string_to_raw IMPORTING iv_string TYPE string iv_encoding TYPE abap_encoding OPTIONAL RETURNING VALUE(rv_xstring) TYPE xstring . CLASS-METHODS dump IMPORTING data TYPE data compress TYPE bool DEFAULT c_bool-false type_descr TYPE REF TO cl_abap_typedescr OPTIONAL pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none assoc_arrays TYPE bool DEFAULT c_bool-false ts_as_iso8601 TYPE bool DEFAULT c_bool-false RETURNING VALUE(r_json) TYPE json . CLASS-METHODS deserialize IMPORTING json TYPE json OPTIONAL jsonx TYPE xstring OPTIONAL pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none assoc_arrays TYPE bool DEFAULT c_bool-false assoc_arrays_opt TYPE bool DEFAULT c_bool-false name_mappings TYPE name_mappings OPTIONAL conversion_exits TYPE bool DEFAULT c_bool-false hex_as_base64 TYPE bool DEFAULT c_bool-true CHANGING data TYPE data . CLASS-METHODS serialize IMPORTING data TYPE data compress TYPE bool DEFAULT c_bool-false name TYPE string OPTIONAL pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none type_descr TYPE REF TO cl_abap_typedescr OPTIONAL assoc_arrays TYPE bool DEFAULT c_bool-false ts_as_iso8601 TYPE bool DEFAULT c_bool-false expand_includes TYPE bool DEFAULT c_bool-true assoc_arrays_opt TYPE bool DEFAULT c_bool-false numc_as_string TYPE bool DEFAULT c_bool-false name_mappings TYPE name_mappings OPTIONAL conversion_exits TYPE bool DEFAULT c_bool-false format_output TYPE bool DEFAULT c_bool-false hex_as_base64 TYPE bool DEFAULT c_bool-true RETURNING VALUE(r_json) TYPE json . METHODS deserialize_int IMPORTING json TYPE json OPTIONAL jsonx TYPE xstring OPTIONAL CHANGING data TYPE data RAISING cx_sy_move_cast_error . CLASS-METHODS generate IMPORTING json TYPE json pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none name_mappings TYPE name_mappings OPTIONAL RETURNING VALUE(rr_data) TYPE REF TO data . METHODS serialize_int IMPORTING data TYPE data name TYPE string OPTIONAL type_descr TYPE REF TO cl_abap_typedescr OPTIONAL RETURNING VALUE(r_json) TYPE json . METHODS generate_int IMPORTING json TYPE json VALUE(length) TYPE i OPTIONAL CHANGING data TYPE REF TO data offset TYPE i DEFAULT 0 RAISING cx_sy_move_cast_error . METHODS constructor IMPORTING compress TYPE bool DEFAULT c_bool-false pretty_name TYPE pretty_name_mode DEFAULT pretty_mode-none assoc_arrays TYPE bool DEFAULT c_bool-false ts_as_iso8601 TYPE bool DEFAULT c_bool-false expand_includes TYPE bool DEFAULT c_bool-true assoc_arrays_opt TYPE bool DEFAULT c_bool-false strict_mode TYPE bool DEFAULT c_bool-false numc_as_string TYPE bool DEFAULT c_bool-false name_mappings TYPE name_mappings OPTIONAL conversion_exits TYPE bool DEFAULT c_bool-false format_output TYPE bool DEFAULT c_bool-false hex_as_base64 TYPE bool DEFAULT c_bool-true bool_types TYPE string DEFAULT mc_bool_types bool_3state TYPE string DEFAULT mc_bool_3state initial_ts TYPE string DEFAULT `""` initial_date TYPE string DEFAULT `""` initial_time TYPE string DEFAULT `""` . CLASS-METHODS bool_to_tribool IMPORTING iv_bool TYPE bool RETURNING VALUE(rv_tribool) TYPE tribool . CLASS-METHODS tribool_to_bool IMPORTING iv_tribool TYPE tribool RETURNING VALUE(rv_bool) TYPE bool . PROTECTED SECTION. TYPES: BEGIN OF t_s_field_cache, name TYPE string, type TYPE REF TO cl_abap_datadescr, elem_type TYPE REF TO cl_abap_elemdescr, convexit_out TYPE string, convexit_in TYPE string, value TYPE REF TO data, END OF t_s_field_cache , BEGIN OF t_s_symbol, header TYPE string, compressable TYPE abap_bool, read_only TYPE abap_bool. INCLUDE TYPE t_s_field_cache. TYPES: END OF t_s_symbol , t_t_symbol TYPE STANDARD TABLE OF t_s_symbol WITH DEFAULT KEY , t_t_field_cache TYPE HASHED TABLE OF t_s_field_cache WITH UNIQUE KEY name , name_mappings_ex TYPE HASHED TABLE OF name_mapping WITH UNIQUE KEY json . TYPES: BEGIN OF t_s_name_value, name TYPE string, value TYPE json, END OF t_s_name_value . TYPES: t_t_name_value TYPE SORTED TABLE OF t_s_name_value WITH UNIQUE KEY name , t_t_json TYPE STANDARD TABLE OF json WITH DEFAULT KEY . TYPES: BEGIN OF t_s_struct_type, keys TYPE string, type TYPE REF TO cl_abap_datadescr, END OF t_s_struct_type . TYPES: t_t_struct_type TYPE SORTED TABLE OF t_s_struct_type WITH UNIQUE KEY keys , BEGIN OF t_s_struct_cache_res, data TYPE REF TO data, symbols TYPE t_t_symbol, END OF t_s_struct_cache_res , BEGIN OF t_s_struct_cache, type_descr TYPE REF TO cl_abap_structdescr, include_aliases TYPE abap_bool, level TYPE i, result TYPE t_s_struct_cache_res, END OF t_s_struct_cache , t_t_struct_cache TYPE HASHED TABLE OF t_s_struct_cache WITH UNIQUE KEY type_descr include_aliases level . CONSTANTS mc_default_indent TYPE string VALUE ` `. "#EC NOTEXT DATA mv_bool_types TYPE string. DATA mv_bool_3state TYPE string. DATA mv_initial_ts TYPE string VALUE `""`. "#EC NOTEXT DATA mv_initial_date TYPE string VALUE `""`. "#EC NOTEXT DATA mv_initial_time TYPE string VALUE `""`. "#EC NOTEXT DATA mv_compress TYPE bool . DATA mv_pretty_name TYPE pretty_name_mode . DATA mv_assoc_arrays TYPE bool . DATA mv_ts_as_iso8601 TYPE bool . DATA mv_expand_includes TYPE bool . DATA mv_assoc_arrays_opt TYPE bool . DATA mv_strict_mode TYPE bool . DATA mv_numc_as_string TYPE bool . DATA mv_format_output TYPE bool . DATA mv_conversion_exits TYPE bool . DATA mv_hex_as_base64 TYPE bool . DATA mt_name_mappings TYPE name_mappings . DATA mt_name_mappings_ex TYPE name_mappings_ex . DATA mt_struct_type TYPE t_t_struct_type . DATA mt_struct_cache TYPE t_t_struct_cache . CLASS-DATA mc_name_symbols_map TYPE string VALUE ` _/_\_:_;_~_._,_-_+_=_>_<_|_(_)_[_]_{_}_@_+_*_?__&_$_#_%_^_'_`. "#EC NOTEXT CLASS-DATA so_type_s TYPE REF TO cl_abap_elemdescr . CLASS-DATA so_type_f TYPE REF TO cl_abap_elemdescr . CLASS-DATA so_type_p TYPE REF TO cl_abap_elemdescr . CLASS-DATA so_type_i TYPE REF TO cl_abap_elemdescr . CLASS-DATA so_type_b TYPE REF TO cl_abap_elemdescr . CLASS-DATA so_type_t_json TYPE REF TO cl_abap_tabledescr . CLASS-DATA so_type_t_name_value TYPE REF TO cl_abap_tabledescr . CLASS-METHODS unescape IMPORTING escaped TYPE string RETURNING VALUE(unescaped) TYPE string . CLASS-METHODS get_convexit_func IMPORTING elem_descr TYPE REF TO cl_abap_elemdescr input TYPE abap_bool OPTIONAL RETURNING VALUE(rv_func) TYPE string . METHODS dump_symbols FINAL IMPORTING it_symbols TYPE t_t_symbol opt_array TYPE bool OPTIONAL format_scope TYPE bool DEFAULT abap_true level TYPE i RETURNING VALUE(r_json) TYPE json . METHODS get_symbols_struct FINAL IMPORTING type_descr TYPE REF TO cl_abap_structdescr include_aliases TYPE abap_bool DEFAULT abap_false data TYPE REF TO data OPTIONAL level TYPE i DEFAULT 0 RETURNING VALUE(result) TYPE t_s_struct_cache_res . METHODS get_symbols_class FINAL IMPORTING type_descr TYPE REF TO cl_abap_classdescr object TYPE REF TO object OPTIONAL RETURNING VALUE(result) TYPE t_t_symbol . METHODS get_symbols FINAL IMPORTING type_descr TYPE REF TO cl_abap_typedescr data TYPE REF TO data OPTIONAL object TYPE REF TO object OPTIONAL include_aliases TYPE abap_bool DEFAULT abap_false RETURNING VALUE(result) TYPE t_t_symbol . METHODS get_fields FINAL IMPORTING type_descr TYPE REF TO cl_abap_typedescr data TYPE REF TO data OPTIONAL object TYPE REF TO object OPTIONAL RETURNING VALUE(rt_fields) TYPE t_t_field_cache . METHODS dump_int IMPORTING data TYPE data type_descr TYPE REF TO cl_abap_typedescr OPTIONAL convexit TYPE string OPTIONAL level TYPE i DEFAULT 0 RETURNING VALUE(r_json) TYPE json . METHODS is_compressable IMPORTING type_descr TYPE REF TO cl_abap_typedescr ##NEEDED name TYPE csequence ##NEEDED RETURNING VALUE(rv_compress) TYPE abap_bool . METHODS restore IMPORTING json TYPE json length TYPE i VALUE(type_descr) TYPE REF TO cl_abap_typedescr OPTIONAL field_cache TYPE t_t_field_cache OPTIONAL CHANGING data TYPE data OPTIONAL offset TYPE i DEFAULT 0 RAISING cx_sy_move_cast_error . METHODS restore_type IMPORTING json TYPE json length TYPE i VALUE(type_descr) TYPE REF TO cl_abap_typedescr OPTIONAL field_cache TYPE t_t_field_cache OPTIONAL convexit TYPE string OPTIONAL CHANGING data TYPE data OPTIONAL offset TYPE i DEFAULT 0 RAISING cx_sy_move_cast_error . METHODS dump_type IMPORTING data TYPE data type_descr TYPE REF TO cl_abap_elemdescr convexit TYPE string RETURNING VALUE(r_json) TYPE json . METHODS dump_type_ex IMPORTING data TYPE data RETURNING VALUE(r_json) TYPE json . METHODS pretty_name_ex IMPORTING in TYPE csequence RETURNING VALUE(out) TYPE string . METHODS generate_int_ex FINAL IMPORTING json TYPE json length TYPE i CHANGING data TYPE data offset TYPE i . METHODS pretty_name IMPORTING in TYPE csequence RETURNING VALUE(out) TYPE string . CLASS-METHODS escape ##SHADOW[ESCAPE] IMPORTING in TYPE any RETURNING VALUE(out) TYPE string . CLASS-METHODS edm_datetime_to_ts IMPORTING ticks TYPE string offset TYPE string OPTIONAL typekind TYPE abap_typekind RETURNING VALUE(r_data) TYPE string . CLASS-METHODS get_indent IMPORTING level TYPE i DEFAULT 0 RETURNING VALUE(indent) TYPE string . METHODS generate_struct CHANGING fields TYPE t_t_name_value data TYPE REF TO data . PRIVATE SECTION. DATA mv_extended TYPE bool . CLASS-DATA mc_me_type TYPE string . CLASS-DATA mc_cov_error TYPE c . ENDCLASS. DEFINE escape_json. &2 = &1. * replace all occurrences of regex `[\\"]` in &1 with `\\$0`. <-- this is slower than 2 plain replaces REPLACE ALL OCCURRENCES OF `\` IN &2 WITH `\\`. REPLACE ALL OCCURRENCES OF `"` IN &2 WITH `\"`. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN &2 WITH `\r\n`. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN &2 WITH `\n`. REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>horizontal_tab IN &2 WITH `\t`. END-OF-DEFINITION. DEFINE is_compressable. IF mv_compress EQ abap_false. &3 = abap_false. ELSEIF mv_extended IS INITIAL. &3 = abap_true. ELSE. &3 = is_compressable( type_descr = &1 name = &2 ). ENDIF. END-OF-DEFINITION. DEFINE dump_type. IF mv_extended IS INITIAL. dump_type_int &1 &2 &3 &4. ELSE. &3 = dump_type( data = &1 type_descr = &2 convexit = &4 ). ENDIF. END-OF-DEFINITION. DEFINE xstring_to_string_int. IF mv_hex_as_base64 IS INITIAL. MOVE &1 TO &2. ELSE. &2 = xstring_to_string( &1 ). ENDIF. END-OF-DEFINITION. DEFINE string_to_xstring_int. IF mv_hex_as_base64 IS INITIAL. MOVE &1 TO &2. ELSE. string_to_xstring( EXPORTING in = &1 CHANGING out = &2 ). ENDIF. END-OF-DEFINITION. DEFINE format_list_output. IF mv_format_output EQ abap_true AND &2 IS NOT INITIAL. CONCATENATE `,` lv_indent INTO lv_lb. CONCATENATE LINES OF &2 INTO &4 SEPARATED BY lv_lb. CONCATENATE &1 lv_indent &4 indent &3 INTO &4. ELSE. CONCATENATE LINES OF &2 INTO &4 SEPARATED BY `,`. CONCATENATE &1 &4 &3 INTO &4. ENDIF. END-OF-DEFINITION. " format_list_output DEFINE dump_type_int. IF &4 IS NOT INITIAL AND &1 IS NOT INITIAL. TRY. CALL FUNCTION &4 EXPORTING input = &1 IMPORTING output = &3 EXCEPTIONS OTHERS = 1. IF sy-subrc IS INITIAL. CONCATENATE `"` &3 `"` INTO &3. ENDIF. CATCH cx_root. "#EC NO_HANDLER ENDTRY. ELSE. CASE &2->type_kind. WHEN cl_abap_typedescr=>typekind_float OR cl_abap_typedescr=>typekind_int OR cl_abap_typedescr=>typekind_int1 OR cl_abap_typedescr=>typekind_int2 OR cl_abap_typedescr=>typekind_packed OR `8`. " TYPEKIND_INT8 -> '8' only from 7.40. IF &2->type_kind EQ cl_abap_typedescr=>typekind_packed AND mv_ts_as_iso8601 EQ c_bool-true AND &2->absolute_name CP `\TYPE=TIMESTAMP*`. IF &1 IS INITIAL. &3 = mv_initial_ts. ELSE. &3 = &1. IF &2->absolute_name EQ `\TYPE=TIMESTAMP`. CONCATENATE `"` &3(4) `-` &3+4(2) `-` &3+6(2) `T` &3+8(2) `:` &3+10(2) `:` &3+12(2) `.0000000Z"` INTO &3. ELSEIF &2->absolute_name EQ `\TYPE=TIMESTAMPL`. CONCATENATE `"` &3(4) `-` &3+4(2) `-` &3+6(2) `T` &3+8(2) `:` &3+10(2) `:` &3+12(2) `.` &3+15(7) `Z"` INTO &3. ENDIF. ENDIF. ELSEIF &1 IS INITIAL. &3 = `0`. ELSE. &3 = &1. IF &1 LT 0. IF &2->type_kind <> cl_abap_typedescr=>typekind_float. "float: sign is already at the beginning SHIFT &3 RIGHT CIRCULAR. ENDIF. ELSE. CONDENSE &3. ENDIF. ENDIF. WHEN cl_abap_typedescr=>typekind_num. IF mv_numc_as_string EQ abap_true. IF &1 IS INITIAL. &3 = `""`. ELSE. CONCATENATE `"` &1 `"` INTO &3. ENDIF. ELSE. &3 = &1. SHIFT &3 LEFT DELETING LEADING ` 0`. IF &3 IS INITIAL. &3 = `0`. ENDIF. ENDIF. WHEN cl_abap_typedescr=>typekind_string OR cl_abap_typedescr=>typekind_csequence OR cl_abap_typedescr=>typekind_clike. IF &1 IS INITIAL. &3 = `""`. ELSEIF &2->absolute_name EQ mc_json_type. &3 = &1. ELSE. escape_json &1 &3. CONCATENATE `"` &3 `"` INTO &3. ENDIF. WHEN cl_abap_typedescr=>typekind_xstring OR cl_abap_typedescr=>typekind_hex. IF &1 IS INITIAL. &3 = `""`. ELSE. xstring_to_string_int &1 &3. CONCATENATE `"` &3 `"` INTO &3. ENDIF. WHEN cl_abap_typedescr=>typekind_char. IF &2->output_length EQ 1 AND mv_bool_types CS &2->absolute_name. IF &1 EQ c_bool-true. &3 = `true`. "#EC NOTEXT ELSEIF &1 IS INITIAL AND mv_bool_3state CS &2->absolute_name. &3 = `null`. "#EC NOTEXT ELSE. &3 = `false`. "#EC NOTEXT ENDIF. ELSE. escape_json &1 &3. CONCATENATE `"` &3 `"` INTO &3. ENDIF. WHEN cl_abap_typedescr=>typekind_date. IF &1 IS INITIAL. &3 = mv_initial_date. ELSE. CONCATENATE `"` &1(4) `-` &1+4(2) `-` &1+6(2) `"` INTO &3. ENDIF. WHEN cl_abap_typedescr=>typekind_time. IF &1 IS INITIAL. &3 = mv_initial_time. ELSE. CONCATENATE `"` &1(2) `:` &1+2(2) `:` &1+4(2) `"` INTO &3. ENDIF. WHEN `k`. " cl_abap_typedescr=>typekind_enum &3 = &1. CONCATENATE `"` &3 `"` INTO &3. WHEN OTHERS. IF &1 IS INITIAL. &3 = `null`. "#EC NOTEXT ELSE. &3 = &1. ENDIF. ENDCASE. ENDIF. END-OF-DEFINITION. DEFINE format_name. CASE &2. WHEN pretty_mode-camel_case. &3 = pretty_name( &1 ). WHEN pretty_mode-extended. &3 = pretty_name_ex( &1 ). WHEN pretty_mode-user_low_case. READ TABLE mt_name_mappings WITH TABLE KEY abap = &1 ASSIGNING <cache>. "#EC WARNOK IF sy-subrc IS INITIAL. &3 = <cache>-json. ELSE. &3 = &1. TRANSLATE &3 TO LOWER CASE. "#EC SYNTCHAR ENDIF. WHEN pretty_mode-user. READ TABLE mt_name_mappings WITH TABLE KEY abap = &1 ASSIGNING <cache>. "#EC WARNOK IF sy-subrc IS INITIAL. &3 = <cache>-json. ELSE. &3 = &1. ENDIF. WHEN pretty_mode-low_case. &3 = &1. TRANSLATE &3 TO LOWER CASE. "#EC SYNTCHAR WHEN OTHERS. &3 = &1. ENDCASE. END-OF-DEFINITION. DEFINE restore_reference. CREATE DATA data TYPE HANDLE &1. ASSIGN data->* TO <data>. restore_type( EXPORTING json = json length = length type_descr = &1 CHANGING offset = offset data = <data> ). END-OF-DEFINITION. DEFINE throw_error. RAISE EXCEPTION TYPE cx_sy_move_cast_error. END-OF-DEFINITION. DEFINE while_offset_cs. WHILE offset < length. FIND FIRST OCCURRENCE OF json+offset(1) IN &1. IF sy-subrc IS NOT INITIAL. EXIT. ENDIF. offset = offset + 1. ENDWHILE. END-OF-DEFINITION. DEFINE while_offset_not_cs. WHILE offset < length. FIND FIRST OCCURRENCE OF &2+offset(1) IN &1. IF sy-subrc IS INITIAL. EXIT. ENDIF. offset = offset + 1. ENDWHILE. END-OF-DEFINITION. DEFINE eat_white. while_offset_cs sv_white_space. IF offset GE length. throw_error. ENDIF. END-OF-DEFINITION. DEFINE eat_name. IF json+offset(1) EQ `"`. mark = offset + 1. offset = mark. FIND FIRST OCCURRENCE OF `"` IN SECTION OFFSET offset OF json MATCH OFFSET offset. IF sy-subrc IS NOT INITIAL. throw_error. ENDIF. match = offset - mark. &1 = json+mark(match). offset = offset + 1. ELSE. throw_error. ENDIF. END-OF-DEFINITION. DEFINE eat_string. IF json+offset(1) EQ `"`. mark = offset + 1. offset = mark. IF json+mark(1) EQ `"`. CLEAR &1. ELSE. DO. FIND FIRST OCCURRENCE OF `"` IN SECTION OFFSET offset OF json MATCH OFFSET pos. IF sy-subrc IS NOT INITIAL. throw_error. ENDIF. offset = pos. pos = pos - 1. " if escaped search further WHILE pos GE 0 AND json+pos(1) EQ `\`. pos = pos - 1. ENDWHILE. match = ( offset - pos ) MOD 2. IF match NE 0. EXIT. ENDIF. offset = offset + 1. ENDDO. match = offset - mark. &1 = json+mark(match). " unescaped singe characters, e.g \\, \", \/ etc, " BUT ONLY if someone really need the data IF type_descr IS NOT INITIAL. &1 = unescape( &1 ). ENDIF. ENDIF. offset = offset + 1. ELSE. throw_error. ENDIF. END-OF-DEFINITION. DEFINE eat_number. mark = offset. while_offset_cs `0123456789+-eE.`. "#EC NOTEXT match = offset - mark. &1 = json+mark(match). END-OF-DEFINITION. DEFINE eat_bool. mark = offset. while_offset_cs `aeflnrstu`. "#EC NOTEXT match = offset - mark. IF json+mark(match) EQ `true`. "#EC NOTEXT &1 = c_bool-true. ELSEIF json+mark(match) EQ `false`. "#EC NOTEXT IF type_descr IS BOUND AND mv_bool_3state CS type_descr->absolute_name. &1 = c_tribool-false. ELSE. &1 = c_bool-false. ENDIF. ELSEIF json+mark(match) EQ `null`. "#EC NOTEXT CLEAR &1. ENDIF. END-OF-DEFINITION. DEFINE eat_char. IF offset < length AND json+offset(1) EQ &1. offset = offset + 1. ELSE. throw_error. ENDIF. END-OF-DEFINITION. CLASS zcl_json IMPLEMENTATION. METHOD bool_to_tribool. IF iv_bool EQ c_bool-true. rv_tribool = c_tribool-true. ELSEIF iv_bool EQ abap_undefined. " fall back for abap _bool rv_tribool = c_tribool-undefined. ELSE. rv_tribool = c_tribool-false. ENDIF. ENDMETHOD. "bool_to_tribool METHOD class_constructor. DATA: lo_bool_type_descr TYPE REF TO cl_abap_typedescr, lo_tribool_type_descr TYPE REF TO cl_abap_typedescr, lo_json_type_descr TYPE REF TO cl_abap_typedescr, lv_pos LIKE sy-fdpos, lv_json_string TYPE json. lo_bool_type_descr = cl_abap_typedescr=>describe_by_data( c_bool-true ). lo_tribool_type_descr = cl_abap_typedescr=>describe_by_data( c_tribool-true ). lo_json_type_descr = cl_abap_typedescr=>describe_by_data( lv_json_string ). CONCATENATE mc_bool_types lo_bool_type_descr->absolute_name lo_tribool_type_descr->absolute_name INTO mc_bool_types. CONCATENATE mc_bool_3state lo_tribool_type_descr->absolute_name INTO mc_bool_3state. CONCATENATE mc_json_type lo_json_type_descr->absolute_name INTO mc_json_type. FIND FIRST OCCURRENCE OF `\TYPE=` IN lo_json_type_descr->absolute_name MATCH OFFSET lv_pos. IF sy-subrc IS INITIAL. mc_me_type = lo_json_type_descr->absolute_name(lv_pos). ENDIF. sv_white_space = cl_abap_char_utilities=>get_simple_spaces_for_cur_cp( ). mc_cov_error = cl_abap_conv_in_ce=>uccp( '0000' ). so_type_s = cl_abap_elemdescr=>get_string( ). so_type_f = cl_abap_elemdescr=>get_f( ). so_type_p ?= cl_abap_typedescr=>describe_by_name( 'p' ). so_type_i = cl_abap_elemdescr=>get_i( ). so_type_b ?= cl_abap_typedescr=>describe_by_name( 'ABAP_BOOL' ). so_type_t_json ?= cl_abap_typedescr=>describe_by_name( 'T_T_JSON' ). so_type_t_name_value ?= cl_abap_typedescr=>describe_by_name( 'T_T_NAME_VALUE' ). ENDMETHOD. "class_constructor METHOD constructor. DATA: rtti TYPE REF TO cl_abap_classdescr, pair LIKE LINE OF name_mappings. mv_compress = compress. mv_pretty_name = pretty_name. mv_assoc_arrays = assoc_arrays. mv_ts_as_iso8601 = ts_as_iso8601. mv_expand_includes = expand_includes. mv_assoc_arrays_opt = assoc_arrays_opt. mv_strict_mode = strict_mode. mv_numc_as_string = numc_as_string. mv_conversion_exits = conversion_exits. mv_format_output = format_output. mv_hex_as_base64 = hex_as_base64. mv_bool_types = bool_types. mv_bool_3state = bool_3state. mv_initial_ts = initial_ts. mv_initial_date = initial_date. mv_initial_time = initial_time. LOOP AT name_mappings INTO pair. TRANSLATE pair-abap TO UPPER CASE. INSERT pair INTO TABLE mt_name_mappings. ENDLOOP. " if it dumps here, you have passed ambiguous mapping to the API " please check your code for duplicates, pairs ABAP - JSON shall be unique INSERT LINES OF mt_name_mappings INTO TABLE mt_name_mappings_ex. IF mt_name_mappings IS NOT INITIAL. IF mv_pretty_name EQ pretty_mode-none. mv_pretty_name = pretty_mode-user. ELSEIF pretty_name EQ pretty_mode-low_case. mv_pretty_name = pretty_mode-user_low_case. ENDIF. ENDIF. rtti ?= cl_abap_classdescr=>describe_by_object_ref( me ). IF rtti->absolute_name NE mc_me_type. mv_extended = c_bool-true. ENDIF. ENDMETHOD. METHOD deserialize. DATA: lo_json TYPE REF TO zcl_json. IF json IS NOT INITIAL OR jsonx IS NOT INITIAL. CREATE OBJECT lo_json EXPORTING pretty_name = pretty_name name_mappings = name_mappings assoc_arrays = assoc_arrays conversion_exits = conversion_exits hex_as_base64 = hex_as_base64 assoc_arrays_opt = assoc_arrays_opt. TRY . lo_json->deserialize_int( EXPORTING json = json jsonx = jsonx CHANGING data = data ). CATCH cx_sy_move_cast_error. "#EC NO_HANDLER ENDTRY. ENDIF. ENDMETHOD. "deserialize METHOD deserialize_int. DATA: length TYPE i, offset TYPE i, unescaped LIKE json. IF json IS NOT INITIAL OR jsonx IS NOT INITIAL. IF jsonx IS NOT INITIAL. unescaped = raw_to_string( jsonx ). ELSE. unescaped = json. ENDIF. " skip leading BOM signs length = strlen( unescaped ). while_offset_not_cs `"{[` unescaped. restore_type( EXPORTING json = unescaped length = length CHANGING data = data offset = offset ). ENDIF. ENDMETHOD. "deserialize METHOD dump. DATA: lo_json TYPE REF TO zcl_json. CREATE OBJECT lo_json EXPORTING compress = compress pretty_name = pretty_name assoc_arrays = assoc_arrays ts_as_iso8601 = ts_as_iso8601. r_json = lo_json->dump_int( data = data type_descr = type_descr ). ENDMETHOD. "dump METHOD dump_int. DATA: lo_typedesc TYPE REF TO cl_abap_typedescr, lo_elem_descr TYPE REF TO cl_abap_elemdescr, lo_classdesc TYPE REF TO cl_abap_classdescr, lo_structdesc TYPE REF TO cl_abap_structdescr, lo_tabledescr TYPE REF TO cl_abap_tabledescr, ls_struct_sym TYPE t_s_struct_cache_res, lt_symbols TYPE t_t_symbol, lt_keys TYPE STANDARD TABLE OF REF TO data WITH DEFAULT KEY, lt_properties TYPE STANDARD TABLE OF string, lo_obj_ref TYPE REF TO object, lo_data_ref TYPE REF TO data, ls_skip_key TYPE LINE OF abap_keydescr_tab, lv_array_opt TYPE abap_bool, indent TYPE string, lv_indent LIKE indent, lv_level LIKE level, lv_lb TYPE string, lv_prop_name TYPE string, lv_keyval TYPE string, lv_itemval TYPE string. FIELD-SYMBOLS: <line> TYPE any, <value> TYPE any, <data> TYPE data, <key> TYPE LINE OF abap_keydescr_tab, <symbol> TYPE t_s_symbol, <table> TYPE ANY TABLE. " increase hierarchy level lv_level = level + 1. " we need here macro instead of method calls because of the performance reasons. " based on SAT measurements. CASE type_descr->kind. WHEN cl_abap_typedescr=>kind_ref. IF data IS INITIAL. r_json = `null`. "#EC NOTEXT ELSEIF type_descr->type_kind EQ cl_abap_typedescr=>typekind_dref. lo_data_ref ?= data. lo_typedesc = cl_abap_typedescr=>describe_by_data_ref( lo_data_ref ). ASSIGN lo_data_ref->* TO <data>. r_json = dump_int( data = <data> type_descr = lo_typedesc level = level ). ELSE. lo_obj_ref ?= data. lo_classdesc ?= cl_abap_typedescr=>describe_by_object_ref( lo_obj_ref ). lt_symbols = get_symbols_class( type_descr = lo_classdesc object = lo_obj_ref ). r_json = dump_symbols( it_symbols = lt_symbols level = level ). ENDIF. WHEN cl_abap_typedescr=>kind_elem. lo_elem_descr ?= type_descr. dump_type data lo_elem_descr r_json convexit. WHEN cl_abap_typedescr=>kind_struct. lo_structdesc ?= type_descr. ls_struct_sym = get_symbols_struct( type_descr = lo_structdesc level = level ). ASSIGN ls_struct_sym-data->* TO <data>. <data> = data. r_json = dump_symbols( it_symbols = ls_struct_sym-symbols level = level ). WHEN cl_abap_typedescr=>kind_table. lo_tabledescr ?= type_descr. lo_typedesc = lo_tabledescr->get_table_line_type( ). ASSIGN data TO <table>. IF mv_format_output EQ abap_true. indent = get_indent( level ). lv_indent = get_indent( lv_level ). ENDIF. " optimization for structured tables IF lo_typedesc->kind EQ cl_abap_typedescr=>kind_struct. lo_structdesc ?= lo_typedesc. ls_struct_sym = get_symbols_struct( type_descr = lo_structdesc level = level ). ASSIGN ls_struct_sym-data->* TO <line>. " here we have differentiation of output of simple table to JSON array " and sorted or hashed table with unique key into JSON associative array IF lo_tabledescr->has_unique_key IS NOT INITIAL AND mv_assoc_arrays IS NOT INITIAL. IF lo_tabledescr->key_defkind EQ lo_tabledescr->keydefkind_user. LOOP AT lo_tabledescr->key ASSIGNING <key>. READ TABLE ls_struct_sym-symbols WITH KEY name = <key>-name ASSIGNING <symbol>. APPEND <symbol>-value TO lt_keys. ENDLOOP. ENDIF. IF lines( lo_tabledescr->key ) EQ 1. READ TABLE lo_tabledescr->key INDEX 1 INTO ls_skip_key. DELETE ls_struct_sym-symbols WHERE name EQ ls_skip_key-name. " remove object wrapping for simple name-value tables IF mv_assoc_arrays_opt EQ abap_true AND lines( ls_struct_sym-symbols ) EQ 1. lv_array_opt = abap_true. ENDIF. ENDIF. LOOP AT <table> INTO <line>. CLEAR: lv_prop_name. " construct key attribute name IF lo_tabledescr->key_defkind EQ lo_tabledescr->keydefkind_user. LOOP AT lt_keys INTO lo_data_ref. ASSIGN lo_data_ref->* TO <value>. lv_keyval = <value>. CONDENSE lv_keyval. IF lv_prop_name IS NOT INITIAL. CONCATENATE lv_prop_name mc_key_separator lv_keyval INTO lv_prop_name. ELSE. lv_prop_name = lv_keyval. ENDIF. ENDLOOP. ELSE. LOOP AT ls_struct_sym-symbols ASSIGNING <symbol>. ASSIGN <symbol>-value->* TO <value>. lv_keyval = <value>. CONDENSE lv_keyval. IF lv_prop_name IS NOT INITIAL. CONCATENATE lv_prop_name mc_key_separator lv_keyval INTO lv_prop_name. ELSE. lv_prop_name = lv_keyval. ENDIF. ENDLOOP. ENDIF. lv_itemval = dump_symbols( it_symbols = ls_struct_sym-symbols opt_array = lv_array_opt format_scope = abap_false level = lv_level ). IF lv_array_opt EQ abap_true. IF mv_format_output EQ abap_true AND lv_itemval IS NOT INITIAL. CONCATENATE `"` lv_prop_name `": ` lv_itemval INTO lv_itemval. ELSE. CONCATENATE `"` lv_prop_name `":` lv_itemval INTO lv_itemval. ENDIF. ELSE. IF mv_format_output EQ abap_true AND lv_itemval IS NOT INITIAL. CONCATENATE `"` lv_prop_name `": {` lv_itemval lv_indent `}` INTO lv_itemval. ELSE. CONCATENATE `"` lv_prop_name `":{` lv_itemval `}` INTO lv_itemval. ENDIF. ENDIF. APPEND lv_itemval TO lt_properties. ENDLOOP. format_list_output `{` lt_properties `}` r_json. ELSE. LOOP AT <table> INTO <line>. lv_itemval = dump_symbols( it_symbols = ls_struct_sym-symbols level = lv_level ). APPEND lv_itemval TO lt_properties. ENDLOOP. format_list_output `[` lt_properties `]` r_json. ENDIF. ELSE. LOOP AT <table> ASSIGNING <value>. lv_itemval = dump_int( data = <value> type_descr = lo_typedesc level = lv_level ). APPEND lv_itemval TO lt_properties. ENDLOOP. format_list_output `[` lt_properties `]` r_json. ENDIF. ENDCASE. ENDMETHOD. "dump METHOD dump_symbols. DATA: lt_fields TYPE STANDARD TABLE OF string, lv_indent TYPE string, lv_level LIKE level, lv_itemval TYPE string. FIELD-SYMBOLS: <value> TYPE any, <symbol> LIKE LINE OF it_symbols. " increase hierarchy level lv_level = level + 1. IF mv_format_output EQ abap_true AND opt_array EQ abap_false. lv_indent = get_indent( lv_level ). ENDIF. LOOP AT it_symbols ASSIGNING <symbol>. ASSIGN <symbol>-value->* TO <value>. CHECK <symbol>-compressable EQ abap_false OR <value> IS NOT INITIAL OR opt_array EQ abap_true. IF <symbol>-elem_type IS NOT INITIAL. dump_type <value> <symbol>-elem_type lv_itemval <symbol>-convexit_out. ELSE. lv_itemval = dump_int( data = <value> type_descr = <symbol>-type convexit = <symbol>-convexit_out level = lv_level ). ENDIF. IF opt_array EQ abap_false. IF mv_format_output EQ abap_true. CONCATENATE lv_indent <symbol>-header lv_itemval INTO lv_itemval. ELSE. CONCATENATE <symbol>-header lv_itemval INTO lv_itemval. ENDIF. ENDIF. APPEND lv_itemval TO lt_fields. ENDLOOP. CONCATENATE LINES OF lt_fields INTO r_json SEPARATED BY `,`. IF format_scope EQ abap_true. IF r_json IS INITIAL. r_json = `{}`. ELSEIF mv_format_output EQ abap_true. lv_indent = get_indent( level ). CONCATENATE `{` r_json lv_indent `}` INTO r_json. ELSE. CONCATENATE `{` r_json `}` INTO r_json. ENDIF. ENDIF. ENDMETHOD. METHOD dump_type. CONSTANTS: lc_typekind_utclong TYPE abap_typekind VALUE 'p', " CL_ABAP_TYPEDESCR=>TYPEKIND_UTCLONG -> 'p' only from 7.60 lc_typekind_int8 TYPE abap_typekind VALUE '8'. " TYPEKIND_INT8 -> '8' only from 7.40 IF convexit IS NOT INITIAL AND data IS NOT INITIAL. TRY. CALL FUNCTION convexit EXPORTING input = data IMPORTING output = r_json EXCEPTIONS OTHERS = 1. IF sy-subrc IS INITIAL. CONCATENATE `"` r_json `"` INTO r_json. ENDIF. CATCH cx_root. "#EC NO_HANDLER ENDTRY. ELSE. CASE type_descr->type_kind. WHEN cl_abap_typedescr=>typekind_float OR cl_abap_typedescr=>typekind_int OR cl_abap_typedescr=>typekind_int1 OR cl_abap_typedescr=>typekind_int2 OR cl_abap_typedescr=>typekind_packed OR lc_typekind_utclong OR lc_typekind_int8. IF mv_ts_as_iso8601 EQ c_bool-true AND ( type_descr->type_kind EQ lc_typekind_utclong OR ( type_descr->type_kind EQ cl_abap_typedescr=>typekind_packed AND type_descr->absolute_name CP `\TYPE=TIMESTAMP*` ) ). IF data IS INITIAL. r_json = mv_initial_ts. ELSE. r_json = data. IF type_descr->absolute_name EQ `\TYPE=TIMESTAMP`. CONCATENATE `"` r_json(4) `-` r_json+4(2) `-` r_json+6(2) `T` r_json+8(2) `:` r_json+10(2) `:` r_json+12(2) `.0000000Z"` INTO r_json. ELSEIF type_descr->absolute_name EQ `\TYPE=TIMESTAMPL`. CONCATENATE `"` r_json(4) `-` r_json+4(2) `-` r_json+6(2) `T` r_json+8(2) `:` r_json+10(2) `:` r_json+12(2) `.` r_json+15(7) `Z"` INTO r_json. ENDIF. ENDIF. ELSEIF data IS INITIAL. r_json = `0`. ELSE. r_json = data. IF data LT 0. IF type_descr->type_kind <> cl_abap_typedescr=>typekind_float. "float: sign is already at the beginning SHIFT r_json RIGHT CIRCULAR. ENDIF. ELSE. CONDENSE r_json. ENDIF. ENDIF. WHEN cl_abap_typedescr=>typekind_num. IF mv_numc_as_string EQ abap_true. IF data IS INITIAL. r_json = `""`. ELSE. CONCATENATE `"` data `"` INTO r_json. ENDIF. ELSE. r_json = data. SHIFT r_json LEFT DELETING LEADING ` 0`. IF r_json IS INITIAL. r_json = `0`. ENDIF. ENDIF. WHEN cl_abap_typedescr=>typekind_string OR cl_abap_typedescr=>typekind_csequence OR cl_abap_typedescr=>typekind_clike. IF data IS INITIAL. r_json = `""`. ELSEIF type_descr->absolute_name EQ mc_json_type. r_json = data. ELSE. r_json = escape( data ). CONCATENATE `"` r_json `"` INTO r_json. ENDIF. WHEN cl_abap_typedescr=>typekind_xstring OR cl_abap_typedescr=>typekind_hex. IF data IS INITIAL. r_json = `""`. ELSE. xstring_to_string_int data r_json. CONCATENATE `"` r_json `"` INTO r_json. ENDIF. WHEN cl_abap_typedescr=>typekind_char. IF type_descr->output_length EQ 1 AND mv_bool_types CS type_descr->absolute_name. IF data EQ c_bool-true. r_json = `true`. "#EC NOTEXT ELSEIF data IS INITIAL AND mv_bool_3state CS type_descr->absolute_name. r_json = `null`. "#EC NOTEXT ELSE. r_json = `false`. "#EC NOTEXT ENDIF. ELSE. r_json = escape( data ). CONCATENATE `"` r_json `"` INTO r_json. ENDIF. WHEN cl_abap_typedescr=>typekind_date. IF data IS INITIAL. r_json = mv_initial_date. ELSE. CONCATENATE `"` data(4) `-` data+4(2) `-` data+6(2) `"` INTO r_json. ENDIF. WHEN cl_abap_typedescr=>typekind_time. IF data IS INITIAL. r_json = mv_initial_time. ELSE. CONCATENATE `"` data(2) `:` data+2(2) `:` data+4(2) `"` INTO r_json. ENDIF. WHEN 'k'. " cl_abap_typedescr=>typekind_enum r_json = data. CONCATENATE `"` r_json `"` INTO r_json. WHEN OTHERS. IF data IS INITIAL. r_json = `null`. "#EC NOTEXT ELSE. r_json = data. ENDIF. ENDCASE. ENDIF. ENDMETHOD. "dump_type METHOD dump_type_ex. DATA: lo_descr TYPE REF TO cl_abap_elemdescr, lv_convexit TYPE string. lo_descr ?= cl_abap_typedescr=>describe_by_data( data ). IF mv_conversion_exits EQ abap_true. lv_convexit = get_convexit_func( elem_descr = lo_descr input = abap_false ). ENDIF. r_json = dump_type( data = data type_descr = lo_descr convexit = lv_convexit ). ENDMETHOD. "DUMP_TYPE_EX METHOD edm_datetime_to_ts. CONSTANTS: lc_epochs TYPE string VALUE `19700101000000`. DATA: lv_ticks TYPE p, lv_seconds TYPE p, lv_subsec TYPE p, lv_timestamps TYPE string, lv_timestamp TYPE timestampl VALUE `19700101000000.0000000`. lv_ticks = ticks. lv_seconds = lv_ticks / 1000. " in seconds lv_subsec = lv_ticks MOD 1000. " in subsec IF lv_subsec GT 0. lv_timestamps = lv_subsec. CONCATENATE lc_epochs `.` lv_timestamps INTO lv_timestamps. lv_timestamp = lv_timestamps. ENDIF. lv_timestamp = cl_abap_tstmp=>add( tstmp = lv_timestamp secs = lv_seconds ). IF offset IS NOT INITIAL. lv_ticks = offset+1. lv_ticks = lv_ticks * 60. "offset is in minutes IF offset(1) = '+'. lv_timestamp = cl_abap_tstmp=>subtractsecs( tstmp = lv_timestamp secs = lv_ticks ). ELSE. lv_timestamp = cl_abap_tstmp=>add( tstmp = lv_timestamp secs = lv_ticks ). ENDIF. ENDIF. CASE typekind. WHEN cl_abap_typedescr=>typekind_time. r_data = lv_timestamp. r_data = r_data+8(6). WHEN cl_abap_typedescr=>typekind_date. r_data = lv_timestamp. r_data = r_data(8). WHEN cl_abap_typedescr=>typekind_packed. r_data = lv_timestamp. ENDCASE. ENDMETHOD. METHOD escape. escape_json in out. ENDMETHOD. "escape METHOD generate. DATA: lo_json TYPE REF TO zcl_json, offset TYPE i, length TYPE i. " skip leading BOM signs length = strlen( json ). while_offset_not_cs `"{[` json. CREATE OBJECT lo_json EXPORTING pretty_name = pretty_name name_mappings = name_mappings assoc_arrays = c_bool-true assoc_arrays_opt = c_bool-true. TRY . lo_json->generate_int( EXPORTING json = json length = length CHANGING offset = offset data = rr_data ). CATCH cx_sy_move_cast_error. "#EC NO_HANDLER ENDTRY. ENDMETHOD. METHOD generate_int. DATA: lt_json TYPE t_t_json, lt_fields TYPE t_t_name_value. FIELD-SYMBOLS: <data> TYPE data, <struct> TYPE data, <json> LIKE LINE OF lt_json, <field> LIKE LINE OF lt_fields, <table> TYPE STANDARD TABLE. IF length IS NOT SUPPLIED. length = strlen( json ). ENDIF. eat_white. CASE json+offset(1). WHEN `{`."result must be a structure restore_type( EXPORTING json = json length = length type_descr = so_type_t_name_value CHANGING offset = offset data = lt_fields ). generate_struct( CHANGING fields = lt_fields data = data ). IF data IS BOUND. ASSIGN data->* TO <struct>. LOOP AT lt_fields ASSIGNING <field>. ASSIGN COMPONENT sy-tabix OF STRUCTURE <struct> TO <data>. generate_int( EXPORTING json = <field>-value CHANGING data = <data> ). ENDLOOP. ENDIF. WHEN `[`."result must be a table of ref restore_type( EXPORTING json = json length = length type_descr = so_type_t_json CHANGING offset = offset data = lt_json ). CREATE DATA data TYPE ref_tab. ASSIGN data->* TO <table>. LOOP AT lt_json ASSIGNING <json>. APPEND INITIAL LINE TO <table> ASSIGNING <data>. generate_int( EXPORTING json = <json> CHANGING data = <data> ). ENDLOOP. WHEN `"`."string restore_reference so_type_s. WHEN `-` OR `0` OR `1` OR `2` OR `3` OR `4` OR `5` OR `6` OR `7` OR `8` OR `9`. " number IF json+offset CS '.'. restore_reference so_type_f. ELSEIF length GT 9. restore_reference so_type_p. ELSE. restore_reference so_type_i. ENDIF. WHEN OTHERS. IF json+offset EQ `true` OR json+offset EQ `false`. "#EC NOTEXT restore_reference so_type_b. ENDIF. ENDCASE. ENDMETHOD. METHOD generate_int_ex. DATA: lv_assoc_arrays LIKE mv_assoc_arrays, lv_assoc_arrays_opt LIKE mv_assoc_arrays_opt. lv_assoc_arrays = mv_assoc_arrays. lv_assoc_arrays_opt = mv_assoc_arrays_opt. mv_assoc_arrays = abap_true. mv_assoc_arrays_opt = abap_true. generate_int( EXPORTING json = json length = length CHANGING offset = offset data = data ). mv_assoc_arrays = lv_assoc_arrays. mv_assoc_arrays_opt = lv_assoc_arrays_opt. ENDMETHOD. METHOD get_convexit_func. DATA: ls_dfies TYPE dfies. elem_descr->get_ddic_field( RECEIVING p_flddescr = ls_dfies " Field Description EXCEPTIONS not_found = 1 no_ddic_type = 2 OTHERS = 3 ). IF sy-subrc IS INITIAL AND ls_dfies-convexit IS NOT INITIAL. IF input EQ abap_true. CONCATENATE 'CONVERSION_EXIT_' ls_dfies-convexit '_INPUT' INTO rv_func. ELSE. CONCATENATE 'CONVERSION_EXIT_' ls_dfies-convexit '_OUTPUT' INTO rv_func. ENDIF. ENDIF. ENDMETHOD. METHOD get_fields. DATA: lt_symbols TYPE t_t_symbol, lv_name TYPE char128, ls_field LIKE LINE OF rt_fields. FIELD-SYMBOLS: <sym> LIKE LINE OF lt_symbols, <cache> LIKE LINE OF mt_name_mappings. lt_symbols = get_symbols( type_descr = type_descr data = data object = object include_aliases = abap_true ). LOOP AT lt_symbols ASSIGNING <sym> WHERE read_only EQ abap_false. MOVE-CORRESPONDING <sym> TO ls_field. " insert as UPPER CASE INSERT ls_field INTO TABLE rt_fields. " insert as lower case TRANSLATE ls_field-name TO LOWER CASE. INSERT ls_field INTO TABLE rt_fields. " as pretty printed IF mv_pretty_name NE pretty_mode-none AND mv_pretty_name NE pretty_mode-low_case. format_name <sym>-name mv_pretty_name ls_field-name. INSERT ls_field INTO TABLE rt_fields. " let us check for not well formed canelCase to be compatible with old logic lv_name = ls_field-name. TRANSLATE lv_name(1) TO UPPER CASE. ls_field-name = lv_name. INSERT ls_field INTO TABLE rt_fields. ENDIF. ENDLOOP. ENDMETHOD. METHOD get_symbols. DATA: class_descr TYPE REF TO cl_abap_classdescr, struct_descr TYPE REF TO cl_abap_structdescr, struct_cache TYPE t_s_struct_cache_res. IF type_descr->kind EQ cl_abap_typedescr=>kind_struct. struct_descr ?= type_descr. struct_cache = get_symbols_struct( type_descr = struct_descr data = data include_aliases = include_aliases ). result = struct_cache-symbols. ELSEIF type_descr->type_kind EQ cl_abap_typedescr=>typekind_class. class_descr ?= type_descr. result = get_symbols_class( type_descr = class_descr object = object ). ENDIF. ENDMETHOD. "GET_SYMBOLS METHOD is_compressable. rv_compress = abap_true. ENDMETHOD. METHOD pretty_name. DATA: tokens TYPE TABLE OF char128, cache LIKE LINE OF mt_name_mappings. FIELD-SYMBOLS: <token> LIKE LINE OF tokens, <cache> LIKE LINE OF mt_name_mappings. READ TABLE mt_name_mappings WITH TABLE KEY abap = in ASSIGNING <cache>. IF sy-subrc IS INITIAL. out = <cache>-json. ELSE. out = in. REPLACE ALL OCCURRENCES OF `__` IN out WITH `*`. TRANSLATE out TO LOWER CASE. TRANSLATE out USING `/_:_~_`. SPLIT out AT `_` INTO TABLE tokens. LOOP AT tokens ASSIGNING <token> FROM 2. TRANSLATE <token>(1) TO UPPER CASE. ENDLOOP. CONCATENATE LINES OF tokens INTO out. REPLACE ALL OCCURRENCES OF `*` IN out WITH `_`. cache-abap = in. cache-json = out. INSERT cache INTO TABLE mt_name_mappings. INSERT cache INTO TABLE mt_name_mappings_ex. ENDIF. ENDMETHOD. "pretty_name METHOD pretty_name_ex. DATA: tokens TYPE TABLE OF char128, cache LIKE LINE OF mt_name_mappings. FIELD-SYMBOLS: <token> LIKE LINE OF tokens, <cache> LIKE LINE OF mt_name_mappings. READ TABLE mt_name_mappings WITH TABLE KEY abap = in ASSIGNING <cache>. IF sy-subrc IS INITIAL. out = <cache>-json. ELSE. out = in. TRANSLATE out TO LOWER CASE. TRANSLATE out USING `/_:_~_`. REPLACE ALL OCCURRENCES OF `__e__` IN out WITH `!`. REPLACE ALL OCCURRENCES OF `__n__` IN out WITH `#`. REPLACE ALL OCCURRENCES OF `__d__` IN out WITH `$`. REPLACE ALL OCCURRENCES OF `__p__` IN out WITH `%`. REPLACE ALL OCCURRENCES OF `__m__` IN out WITH `&`. REPLACE ALL OCCURRENCES OF `__s__` IN out WITH `*`. REPLACE ALL OCCURRENCES OF `__h__` IN out WITH `-`. REPLACE ALL OCCURRENCES OF `__t__` IN out WITH `~`. REPLACE ALL OCCURRENCES OF `__l__` IN out WITH `/`. REPLACE ALL OCCURRENCES OF `__c__` IN out WITH `:`. REPLACE ALL OCCURRENCES OF `__v__` IN out WITH `|`. REPLACE ALL OCCURRENCES OF `__a__` IN out WITH `@`. REPLACE ALL OCCURRENCES OF `__o__` IN out WITH `.`. REPLACE ALL OCCURRENCES OF `___` IN out WITH `.`. REPLACE ALL OCCURRENCES OF `__` IN out WITH `"`. SPLIT out AT `_` INTO TABLE tokens. LOOP AT tokens ASSIGNING <token> FROM 2. TRANSLATE <token>(1) TO UPPER CASE. ENDLOOP. CONCATENATE LINES OF tokens INTO out. REPLACE ALL OCCURRENCES OF `"` IN out WITH `_`. cache-abap = in. cache-json = out. INSERT cache INTO TABLE mt_name_mappings. INSERT cache INTO TABLE mt_name_mappings_ex. ENDIF. ENDMETHOD. "pretty_name_ex METHOD raw_to_string. DATA: lv_output_length TYPE i, lt_binary_tab TYPE STANDARD TABLE OF sdokcntbin. CALL FUNCTION 'SCMS_XSTRING_TO_BINARY' EXPORTING buffer = iv_xstring IMPORTING output_length = lv_output_length TABLES binary_tab = lt_binary_tab. CALL FUNCTION 'SCMS_BINARY_TO_STRING' EXPORTING input_length = lv_output_length encoding = iv_encoding IMPORTING text_buffer = rv_string output_length = lv_output_length TABLES binary_tab = lt_binary_tab. ENDMETHOD. METHOD restore. DATA: mark LIKE offset, match LIKE offset, ref_descr TYPE REF TO cl_abap_refdescr, data_descr TYPE REF TO cl_abap_datadescr, data_ref TYPE REF TO data, object_ref TYPE REF TO object, fields LIKE field_cache, name_json TYPE string. FIELD-SYMBOLS: <value> TYPE any, <field_cache> LIKE LINE OF field_cache. fields = field_cache. IF type_descr IS NOT INITIAL AND type_descr->kind EQ type_descr->kind_ref. ref_descr ?= type_descr. type_descr = ref_descr->get_referenced_type( ). IF ref_descr->type_kind EQ ref_descr->typekind_oref. IF data IS INITIAL. " can fire an exception, if type is abstract or constructor protected CREATE OBJECT data TYPE (type_descr->absolute_name). ELSE. type_descr = cl_abap_typedescr=>describe_by_object_ref( data ). ENDIF. object_ref ?= data. fields = get_fields( type_descr = type_descr object = object_ref ). ELSEIF ref_descr->type_kind EQ ref_descr->typekind_dref. IF data IS INITIAL. data_descr ?= type_descr. CREATE DATA data TYPE HANDLE data_descr. ELSE. type_descr = cl_abap_typedescr=>describe_by_data_ref( data ). ENDIF. data_ref ?= data. ASSIGN data_ref->* TO <value>. fields = get_fields( type_descr = type_descr data = data_ref ). restore( EXPORTING json = json length = length type_descr = type_descr field_cache = fields CHANGING data = <value> offset = offset ). RETURN. ENDIF. ENDIF. IF fields IS INITIAL AND type_descr IS NOT INITIAL AND type_descr->kind EQ type_descr->kind_struct. GET REFERENCE OF data INTO data_ref. fields = get_fields( type_descr = type_descr data = data_ref ). ENDIF. eat_white. eat_char `{`. eat_white. WHILE offset < length AND json+offset(1) NE `}`. eat_name name_json. eat_white. eat_char `:`. eat_white. READ TABLE fields WITH TABLE KEY name = name_json ASSIGNING <field_cache>. IF sy-subrc IS NOT INITIAL. TRANSLATE name_json TO UPPER CASE. READ TABLE fields WITH TABLE KEY name = name_json ASSIGNING <field_cache>. ENDIF. IF sy-subrc IS INITIAL. ASSIGN <field_cache>-value->* TO <value>. restore_type( EXPORTING json = json length = length type_descr = <field_cache>-type convexit = <field_cache>-convexit_in CHANGING data = <value> offset = offset ). ELSE. restore_type( EXPORTING json = json length = length CHANGING offset = offset ). ENDIF. eat_white. IF offset < length AND json+offset(1) NE `}`. eat_char `,`. eat_white. ELSE. EXIT. ENDIF. ENDWHILE. eat_char `}`. ENDMETHOD. "restore METHOD restore_type. DATA: mark LIKE offset, match LIKE offset, sdummy TYPE string, "#EC NEEDED rdummy TYPE REF TO data, "#EC NEEDED pos LIKE offset, line TYPE REF TO data, key_ref TYPE REF TO data, data_ref TYPE REF TO data, key_name TYPE string, key_value TYPE string, lt_fields LIKE field_cache, ls_symbols TYPE t_s_struct_cache_res, lv_ticks TYPE string, lv_offset TYPE string, lv_convexit LIKE convexit, lo_exp TYPE REF TO cx_root, elem_descr TYPE REF TO cl_abap_elemdescr, table_descr TYPE REF TO cl_abap_tabledescr, struct_descr TYPE REF TO cl_abap_structdescr, data_descr TYPE REF TO cl_abap_datadescr. FIELD-SYMBOLS: <line> TYPE any, <value> TYPE any, <data> TYPE data, <field> LIKE LINE OF lt_fields, <table> TYPE ANY TABLE, <value_sym> TYPE t_s_symbol. lv_convexit = convexit. IF type_descr IS INITIAL AND data IS SUPPLIED. type_descr = cl_abap_typedescr=>describe_by_data( data ). IF mv_conversion_exits EQ abap_true AND lv_convexit IS INITIAL AND type_descr->kind EQ cl_abap_typedescr=>kind_elem. elem_descr ?= type_descr. lv_convexit = get_convexit_func( elem_descr = elem_descr input = abap_true ). ENDIF. ENDIF. eat_white. TRY . IF data IS SUPPLIED AND type_descr->absolute_name EQ mc_json_type. " skip deserialization mark = offset. restore_type( EXPORTING json = json length = length CHANGING offset = offset ). match = offset - mark. data = json+mark(match). ELSE. CASE json+offset(1). WHEN `{`. " object IF data IS SUPPLIED. IF mv_assoc_arrays EQ c_bool-true AND type_descr->kind EQ cl_abap_typedescr=>kind_table. table_descr ?= type_descr. data_descr = table_descr->get_table_line_type( ). IF table_descr->has_unique_key IS NOT INITIAL. eat_char `{`. eat_white. IF json+offset(1) NE `}`. ASSIGN data TO <table>. CLEAR <table>. CREATE DATA line LIKE LINE OF <table>. ASSIGN line->* TO <line>. lt_fields = get_fields( type_descr = data_descr data = line ). IF table_descr->key_defkind EQ table_descr->keydefkind_user AND lines( table_descr->key ) EQ 1. READ TABLE table_descr->key INDEX 1 INTO key_name. READ TABLE lt_fields WITH TABLE KEY name = key_name ASSIGNING <field>. key_ref = <field>-value. IF mv_assoc_arrays_opt EQ c_bool-true. struct_descr ?= data_descr. ls_symbols = get_symbols_struct( type_descr = struct_descr data = line ). DELETE ls_symbols-symbols WHERE name EQ key_name. IF lines( ls_symbols-symbols ) EQ 1. READ TABLE ls_symbols-symbols INDEX 1 ASSIGNING <value_sym>. ENDIF. ENDIF. ENDIF. eat_white. WHILE offset < length AND json+offset(1) NE `}`. CLEAR <line>. eat_name key_value. eat_white. eat_char `:`. eat_white. IF <value_sym> IS ASSIGNED. ASSIGN <value_sym>-value->* TO <value>. restore_type( EXPORTING json = json length = length type_descr = <value_sym>-type convexit = <value_sym>-convexit_in CHANGING data = <value> offset = offset ). ELSE. restore_type( EXPORTING json = json length = length type_descr = data_descr field_cache = lt_fields CHANGING data = <line> offset = offset ). ENDIF. IF table_descr->key_defkind EQ table_descr->keydefkind_user. IF key_ref IS BOUND. ASSIGN key_ref->* TO <value>. IF <value> IS INITIAL. <value> = key_value. ENDIF. ENDIF. ELSEIF <line> IS INITIAL. <line> = key_value. ENDIF. INSERT <line> INTO TABLE <table>. eat_white. IF offset < length AND json+offset(1) NE `}`. eat_char `,`. eat_white. ELSE. EXIT. ENDIF. ENDWHILE. ELSE. CLEAR data. ENDIF. eat_char `}`. ELSE. restore( EXPORTING json = json length = length CHANGING offset = offset ). ENDIF. ELSEIF type_descr->type_kind EQ cl_abap_typedescr=>typekind_dref. IF data IS INITIAL. generate_int_ex( EXPORTING json = json length = length CHANGING offset = offset data = data ). ELSE. data_ref ?= data. type_descr = cl_abap_typedescr=>describe_by_data_ref( data_ref ). ASSIGN data_ref->* TO <data>. restore_type( EXPORTING json = json length = length type_descr = type_descr CHANGING data = <data> offset = offset ). ENDIF. ELSE. restore( EXPORTING json = json length = length type_descr = type_descr field_cache = field_cache CHANGING data = data offset = offset ). ENDIF. ELSE. restore( EXPORTING json = json length = length CHANGING offset = offset ). ENDIF. WHEN `[`. " array IF data IS SUPPLIED AND type_descr->type_kind EQ cl_abap_typedescr=>typekind_dref. IF data IS INITIAL. generate_int_ex( EXPORTING json = json length = length CHANGING offset = offset data = data ). ELSE. data_ref ?= data. type_descr = cl_abap_typedescr=>describe_by_data_ref( data_ref ). ASSIGN data_ref->* TO <data>. restore_type( EXPORTING json = json length = length type_descr = type_descr CHANGING data = <data> offset = offset ). ENDIF. ELSE. eat_char `[`. eat_white. IF json+offset(1) NE `]`. IF data IS SUPPLIED AND type_descr->kind EQ cl_abap_typedescr=>kind_table. table_descr ?= type_descr. data_descr = table_descr->get_table_line_type( ). ASSIGN data TO <table>. CLEAR <table>. CREATE DATA line LIKE LINE OF <table>. ASSIGN line->* TO <line>. lt_fields = get_fields( type_descr = data_descr data = line ). WHILE offset < length AND json+offset(1) NE `]`. CLEAR <line>. restore_type( EXPORTING json = json length = length type_descr = data_descr field_cache = lt_fields CHANGING data = <line> offset = offset ). INSERT <line> INTO TABLE <table>. eat_white. IF offset < length AND json+offset(1) NE `]`. eat_char `,`. eat_white. ELSE. EXIT. ENDIF. ENDWHILE. ELSE. " skip array eat_white. WHILE offset < length AND json+offset(1) NE `]`. restore_type( EXPORTING json = json length = length CHANGING offset = offset ). eat_white. IF offset < length AND json+offset(1) NE `]`. eat_char `,`. eat_white. ELSE. EXIT. ENDIF. ENDWHILE. IF data IS SUPPLIED. " JSON to ABAP type match error eat_char `]`. throw_error. ENDIF. ENDIF. ELSEIF data IS SUPPLIED. CLEAR data. ENDIF. eat_char `]`. ENDIF. WHEN `"`. " string eat_string sdummy. IF data IS SUPPLIED. " unescape string IF sdummy IS NOT INITIAL. IF type_descr->kind EQ cl_abap_typedescr=>kind_elem. elem_descr ?= type_descr. IF lv_convexit IS NOT INITIAL. TRY . CALL FUNCTION lv_convexit EXPORTING input = sdummy IMPORTING output = data EXCEPTIONS error_message = 2 OTHERS = 1. IF sy-subrc IS INITIAL. RETURN. ENDIF. CATCH cx_root. "#EC NO_HANDLER ENDTRY. ENDIF. CASE elem_descr->type_kind. WHEN cl_abap_typedescr=>typekind_char. IF elem_descr->output_length EQ 1 AND mv_bool_types CS elem_descr->absolute_name. IF sdummy(1) CA `XxTt1`. data = c_bool-true. ELSE. data = c_bool-false. ENDIF. RETURN. ENDIF. WHEN cl_abap_typedescr=>typekind_xstring. string_to_xstring_int sdummy data. RETURN. WHEN cl_abap_typedescr=>typekind_hex. " support for Edm.Guid REPLACE FIRST OCCURRENCE OF REGEX `^([0-9A-F]{8})-([0-9A-F]{4})-([0-9A-F]{4})-([0-9A-F]{4})-([0-9A-F]{12})$` IN sdummy WITH `$1$2$3$4$5` REPLACEMENT LENGTH match IGNORING CASE. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = sdummy(match). TRANSLATE sdummy TO UPPER CASE. data = sdummy. ELSE. string_to_xstring_int sdummy data. ENDIF. RETURN. WHEN cl_abap_typedescr=>typekind_date. " support for ISO8601 => https://en.wikipedia.org/wiki/ISO_8601 REPLACE FIRST OCCURRENCE OF REGEX `^(\d{4})-(\d{2})-(\d{2})` IN sdummy WITH `$1$2$3` REPLACEMENT LENGTH match. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = sdummy(match). ELSE. " support for Edm.DateTime => http://www.odata.org/documentation/odata-version-2-0/json-format/ FIND FIRST OCCURRENCE OF REGEX `^\/Date\((-?\d+)([+-]\d{1,4})?\)\/` IN sdummy SUBMATCHES lv_ticks lv_offset IGNORING CASE. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = edm_datetime_to_ts( ticks = lv_ticks offset = lv_offset typekind = elem_descr->type_kind ). ELSE. " support for Edm.Time => https://www.w3.org/TR/xmlschema11-2/#nt-durationRep REPLACE FIRST OCCURRENCE OF REGEX `^-?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?` IN sdummy WITH `$1$2$3` REPLACEMENT LENGTH match. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = sdummy(match). ENDIF. ENDIF. ENDIF. WHEN cl_abap_typedescr=>typekind_time. " support for ISO8601 => https://en.wikipedia.org/wiki/ISO_8601 REPLACE FIRST OCCURRENCE OF REGEX `^(\d{2}):(\d{2}):(\d{2})` IN sdummy WITH `$1$2$3` REPLACEMENT LENGTH match. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = sdummy(match). ELSE. " support for Edm.DateTime => http://www.odata.org/documentation/odata-version-2-0/json-format/ FIND FIRST OCCURRENCE OF REGEX '^\/Date\((-?\d+)([+-]\d{1,4})?\)\/' IN sdummy SUBMATCHES lv_ticks lv_offset IGNORING CASE. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = edm_datetime_to_ts( ticks = lv_ticks offset = lv_offset typekind = elem_descr->type_kind ). ELSE. " support for Edm.Time => https://www.w3.org/TR/xmlschema11-2/#nt-durationRep REPLACE FIRST OCCURRENCE OF REGEX `^-?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?` IN sdummy WITH `$4$5$6` REPLACEMENT LENGTH match. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = sdummy(match). ENDIF. ENDIF. ENDIF. WHEN cl_abap_typedescr=>typekind_packed. REPLACE FIRST OCCURRENCE OF REGEX `^(\d{4})-?(\d{2})-?(\d{2})T(\d{2}):?(\d{2}):?(\d{2})(?:[\.,](\d{0,7}))?Z?` IN sdummy WITH `$1$2$3$4$5$6.$7` REPLACEMENT LENGTH match. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = sdummy(match). ELSE. FIND FIRST OCCURRENCE OF REGEX '^\/Date\((-?\d+)([+-]\d{1,4})?\)\/' IN sdummy SUBMATCHES lv_ticks lv_offset IGNORING CASE. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = edm_datetime_to_ts( ticks = lv_ticks offset = lv_offset typekind = elem_descr->type_kind ). ELSE. " support for Edm.Time => https://www.w3.org/TR/xmlschema11-2/#nt-durationRep REPLACE FIRST OCCURRENCE OF REGEX `^-?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?` IN sdummy WITH `$1$2$3$4$5$6.$7` REPLACEMENT LENGTH match. "#EC NOTEXT IF sy-subrc EQ 0. sdummy = sdummy(match). ENDIF. ENDIF. ENDIF. WHEN `k`. "cl_abap_typedescr=>typekind_enum TRY. CALL METHOD ('CL_ABAP_XSD')=>('TO_VALUE') EXPORTING cs = sdummy CHANGING val = data. RETURN. CATCH cx_sy_dyn_call_error. throw_error. " Deserialization of enums is not supported ENDTRY. ENDCASE. ELSEIF type_descr->type_kind EQ cl_abap_typedescr=>typekind_dref. CREATE DATA rdummy TYPE string. ASSIGN rdummy->* TO <data>. <data> = sdummy. data ?= rdummy. RETURN. ELSE. throw_error. " Other wise dumps with OBJECTS_MOVE_NOT_SUPPORTED ENDIF. data = sdummy. ELSEIF type_descr->kind EQ cl_abap_typedescr=>kind_elem. CLEAR data. ELSE. throw_error. " Other wise dumps with OBJECTS_MOVE_NOT_SUPPORTED ENDIF. ENDIF. WHEN `-` OR `0` OR `1` OR `2` OR `3` OR `4` OR `5` OR `6` OR `7` OR `8` OR `9`. " number IF data IS SUPPLIED. IF type_descr->kind EQ type_descr->kind_ref AND type_descr->type_kind EQ cl_abap_typedescr=>typekind_dref. eat_number sdummy. "#EC NOTEXT match = strlen( sdummy ). IF sdummy CS '.'. " float. CREATE DATA rdummy TYPE f. ELSEIF match GT 9. " packed CREATE DATA rdummy TYPE p. ELSE. " integer CREATE DATA rdummy TYPE i. ENDIF. ASSIGN rdummy->* TO <data>. <data> = sdummy. data ?= rdummy. ELSEIF type_descr->kind EQ type_descr->kind_elem. IF lv_convexit IS NOT INITIAL. TRY . eat_number sdummy. "#EC NOTEXT CALL FUNCTION lv_convexit EXPORTING input = sdummy IMPORTING output = data EXCEPTIONS error_message = 2 OTHERS = 1. IF sy-subrc IS INITIAL. RETURN. ENDIF. CATCH cx_root. "#EC NO_HANDLER ENDTRY. ENDIF. eat_number data. "#EC NOTEXT ELSE. eat_number sdummy. "#EC NOTEXT throw_error. ENDIF. ELSE. eat_number sdummy. "#EC NOTEXT ENDIF. WHEN OTHERS. " boolean, e.g true/false/null IF data IS SUPPLIED. IF type_descr->kind EQ type_descr->kind_ref AND type_descr->type_kind EQ cl_abap_typedescr=>typekind_dref. CREATE DATA rdummy TYPE bool. ASSIGN rdummy->* TO <data>. eat_bool <data>. "#EC NOTEXT data ?= rdummy. ELSEIF type_descr->kind EQ type_descr->kind_elem. eat_bool data. "#EC NOTEXT ELSE. eat_bool sdummy. "#EC NOTEXT throw_error. ENDIF. ELSE. eat_bool sdummy. "#EC NOTEXT ENDIF. ENDCASE. ENDIF. CATCH cx_sy_move_cast_error cx_sy_conversion_no_number cx_sy_conversion_overflow INTO lo_exp. CLEAR data. IF mv_strict_mode EQ abap_true. RAISE EXCEPTION TYPE cx_sy_move_cast_error EXPORTING previous = lo_exp. ENDIF. ENDTRY. ENDMETHOD. "restore_type METHOD serialize. DATA: lo_json TYPE REF TO zcl_json. CREATE OBJECT lo_json EXPORTING compress = compress pretty_name = pretty_name name_mappings = name_mappings assoc_arrays = assoc_arrays assoc_arrays_opt = assoc_arrays_opt expand_includes = expand_includes numc_as_string = numc_as_string conversion_exits = conversion_exits format_output = format_output hex_as_base64 = hex_as_base64 ts_as_iso8601 = ts_as_iso8601. r_json = lo_json->serialize_int( name = name data = data type_descr = type_descr ). ENDMETHOD. "serialize METHOD serialize_int. DATA: lo_descr TYPE REF TO cl_abap_typedescr, lo_elem_descr TYPE REF TO cl_abap_elemdescr, lv_convexit TYPE string. IF type_descr IS INITIAL. lo_descr = cl_abap_typedescr=>describe_by_data( data ). ELSE. lo_descr = type_descr. ENDIF. IF mv_conversion_exits EQ abap_true AND lo_descr->kind EQ cl_abap_typedescr=>kind_elem. lo_elem_descr ?= lo_descr. lv_convexit = get_convexit_func( elem_descr = lo_elem_descr input = abap_false ). ENDIF. r_json = dump_int( data = data type_descr = lo_descr convexit = lv_convexit ). IF name IS NOT INITIAL AND ( mv_compress IS INITIAL OR r_json IS NOT INITIAL ). CONCATENATE `"` name `":` r_json INTO r_json. ENDIF. ENDMETHOD. "serialize METHOD string_to_raw. CALL FUNCTION 'SCMS_STRING_TO_XSTRING' EXPORTING text = iv_string encoding = iv_encoding IMPORTING buffer = rv_xstring EXCEPTIONS OTHERS = 1. IF sy-subrc IS NOT INITIAL. CLEAR rv_xstring. ENDIF. ENDMETHOD. METHOD string_to_xstring. DATA: lv_xstring TYPE xstring. CALL FUNCTION 'SSFC_BASE64_DECODE' EXPORTING b64data = in IMPORTING bindata = lv_xstring EXCEPTIONS OTHERS = 1. IF sy-subrc IS INITIAL. out = lv_xstring. ELSE. out = in. ENDIF. ENDMETHOD. "string_to_xstring METHOD tribool_to_bool. IF iv_tribool EQ c_tribool-true. rv_bool = c_bool-true. ELSEIF iv_tribool EQ c_tribool-undefined. rv_bool = abap_undefined. " fall back to abap_undefined ENDIF. ENDMETHOD. "TRIBOOL_TO_BOOL METHOD unescape. DATA: lv_offset TYPE i, lv_match TYPE i, lv_delta TYPE i, lv_length TYPE i, lv_offset_e TYPE i, lv_length_e TYPE i, lv_unicode_symb TYPE c, lv_unicode_escaped TYPE string, lt_matches TYPE match_result_tab. FIELD-SYMBOLS: <match> LIKE LINE OF lt_matches. " see reference for escaping rules in JSON RFC " https://www.ietf.org/rfc/rfc4627.txt unescaped = escaped. lv_length = strlen( unescaped ). FIND FIRST OCCURRENCE OF REGEX `\\[rntfbu]` IN unescaped RESPECTING CASE. IF sy-subrc IS INITIAL. FIND ALL OCCURRENCES OF REGEX `\\.` IN unescaped RESULTS lt_matches RESPECTING CASE. LOOP AT lt_matches ASSIGNING <match>. lv_match = <match>-offset - lv_delta. lv_offset = lv_match + 1. CASE unescaped+lv_offset(1). WHEN `r`. REPLACE SECTION OFFSET lv_match LENGTH 2 OF unescaped WITH cl_abap_char_utilities=>cr_lf(1). lv_delta = lv_delta + 1. WHEN `n`. REPLACE SECTION OFFSET lv_match LENGTH 2 OF unescaped WITH cl_abap_char_utilities=>newline. lv_delta = lv_delta + 1. WHEN `t`. REPLACE SECTION OFFSET lv_match LENGTH 2 OF unescaped WITH cl_abap_char_utilities=>horizontal_tab. lv_delta = lv_delta + 1. WHEN `f`. REPLACE SECTION OFFSET lv_match LENGTH 2 OF unescaped WITH cl_abap_char_utilities=>form_feed. lv_delta = lv_delta + 1. WHEN `b`. REPLACE SECTION OFFSET lv_match LENGTH 2 OF unescaped WITH cl_abap_char_utilities=>backspace. lv_delta = lv_delta + 1. WHEN `u`. lv_offset = lv_offset + 1. lv_offset_e = lv_offset + 4. lv_length_e = lv_length + lv_delta. IF lv_offset_e LE lv_length_e. lv_unicode_escaped = unescaped+lv_offset(4). TRANSLATE lv_unicode_escaped TO UPPER CASE. lv_unicode_symb = cl_abap_conv_in_ce=>uccp( lv_unicode_escaped ). IF lv_unicode_symb NE mc_cov_error. REPLACE SECTION OFFSET lv_match LENGTH 6 OF unescaped WITH lv_unicode_symb. lv_delta = lv_delta + 5. ENDIF. ENDIF. ENDCASE. ENDLOOP. ENDIF. " based on RFC mentioned above, _any_ character can be escaped, and so shall be enscaped " the only exception is Unicode symbols, that shall be kept untouched, while serializer does not handle them " unescaped singe characters, e.g \\, \", \/ etc REPLACE ALL OCCURRENCES OF REGEX `\\(.)` IN unescaped WITH `$1` RESPECTING CASE. ENDMETHOD. METHOD xstring_to_string. DATA: lv_xstring TYPE xstring. " let us fix data conversion issues here lv_xstring = in. CALL FUNCTION 'SSFC_BASE64_ENCODE' EXPORTING bindata = lv_xstring IMPORTING b64data = out EXCEPTIONS OTHERS = 1. IF sy-subrc IS NOT INITIAL. out = in. ENDIF. ENDMETHOD. "xstring_to_string METHOD generate_struct. DATA: lv_comp_name TYPE abap_compname, lt_comp TYPE abap_component_tab, lt_keys TYPE STANDARD TABLE OF string, lv_invalid TYPE abap_bool, ls_type LIKE LINE OF mt_struct_type, lt_names TYPE HASHED TABLE OF string WITH UNIQUE KEY table_line, cache LIKE LINE OF mt_name_mappings_ex, ls_comp LIKE LINE OF lt_comp. FIELD-SYMBOLS: <field> LIKE LINE OF fields, <cache> LIKE LINE OF mt_name_mappings_ex. CHECK fields IS NOT INITIAL. " prepare structure type key LOOP AT fields ASSIGNING <field>. APPEND <field>-name TO lt_keys. ENDLOOP. CONCATENATE LINES OF lt_keys INTO ls_type-keys. READ TABLE mt_struct_type WITH TABLE KEY keys = ls_type-keys INTO ls_type. IF sy-subrc IS NOT INITIAL. ls_comp-type = cl_abap_refdescr=>get_ref_to_data( ). LOOP AT fields ASSIGNING <field>. READ TABLE mt_name_mappings_ex WITH TABLE KEY json = <field>-name ASSIGNING <cache>. IF sy-subrc IS INITIAL. ls_comp-name = <cache>-abap. ELSE. cache-json = ls_comp-name = <field>-name. " remove characters not allowed in component names TRANSLATE ls_comp-name USING mc_name_symbols_map. IF mv_pretty_name EQ pretty_mode-camel_case OR mv_pretty_name EQ pretty_mode-extended. REPLACE ALL OCCURRENCES OF REGEX `([a-z])([A-Z])` IN ls_comp-name WITH `$1_$2`. "#EC NOTEXT ENDIF. TRANSLATE ls_comp-name TO UPPER CASE. cache-abap = ls_comp-name = lv_comp_name = ls_comp-name. " truncate by allowed field name length INSERT cache INTO TABLE mt_name_mappings_ex. ENDIF. INSERT ls_comp-name INTO TABLE lt_names. IF sy-subrc IS INITIAL. APPEND ls_comp TO lt_comp. ELSE. DELETE fields. lv_invalid = abap_true. ENDIF. ENDLOOP. TRY. ls_type-type = cl_abap_structdescr=>create( p_components = lt_comp p_strict = c_bool-false ). CATCH cx_sy_struct_creation. "#EC NO_HANDLER ENDTRY. IF lv_invalid EQ abap_false. INSERT ls_type INTO TABLE mt_struct_type. ENDIF. ENDIF. IF ls_type-type IS NOT INITIAL. TRY. CREATE DATA data TYPE HANDLE ls_type-type. CATCH cx_sy_create_data_error. "#EC NO_HANDLER ENDTRY. ENDIF. ENDMETHOD. METHOD get_indent. STATICS: st_indent TYPE STANDARD TABLE OF string WITH DEFAULT KEY. DATA: lv_filled TYPE i. READ TABLE st_indent INDEX level INTO indent. IF sy-subrc IS NOT INITIAL. lv_filled = lines( st_indent ). indent = cl_abap_char_utilities=>cr_lf. DO level TIMES. CONCATENATE indent mc_default_indent INTO indent. IF sy-index GT lv_filled. APPEND indent TO st_indent. ENDIF. ENDDO. ENDIF. ENDMETHOD. METHOD get_symbols_class. DATA: symb LIKE LINE OF result. FIELD-SYMBOLS: <attr> LIKE LINE OF cl_abap_objectdescr=>attributes, <cache> LIKE LINE OF mt_name_mappings, <field> TYPE any. LOOP AT type_descr->attributes ASSIGNING <attr> WHERE is_constant IS INITIAL AND alias_for IS INITIAL AND ( is_interface IS INITIAL OR type_kind NE cl_abap_typedescr=>typekind_oref ). ASSIGN object->(<attr>-name) TO <field>. CHECK sy-subrc IS INITIAL. " we can only assign to public attributes symb-name = <attr>-name. symb-read_only = <attr>-is_read_only. symb-type = type_descr->get_attribute_type( <attr>-name ). IF symb-type->kind EQ cl_abap_typedescr=>kind_elem. symb-elem_type ?= symb-type. ELSE. CLEAR symb-elem_type. ENDIF. IF mv_conversion_exits EQ abap_true AND symb-elem_type IS NOT INITIAL. symb-convexit_in = get_convexit_func( elem_descr = symb-elem_type input = abap_true ). symb-convexit_out = get_convexit_func( elem_descr = symb-elem_type input = abap_false ). ENDIF. is_compressable symb-type symb-name symb-compressable. GET REFERENCE OF <field> INTO symb-value. format_name symb-name mv_pretty_name symb-header. CONCATENATE `"` symb-header `":` INTO symb-header. IF mv_format_output EQ abap_true. CONCATENATE symb-header ` ` INTO symb-header. ENDIF. APPEND symb TO result. ENDLOOP. ENDMETHOD. "GET_SYMBOLS METHOD get_symbols_struct. DATA: comp_tab TYPE cl_abap_structdescr=>component_table, sym_cache LIKE result, symbol TYPE t_s_symbol, struct_descr TYPE REF TO cl_abap_structdescr, struct_cache LIKE LINE OF mt_struct_cache. FIELD-SYMBOLS: <comp> LIKE LINE OF comp_tab, <symbol> LIKE symbol, <cache> LIKE LINE OF mt_name_mappings, <struct> LIKE LINE OF mt_struct_cache, <data> TYPE data, <field> TYPE any. READ TABLE mt_struct_cache WITH TABLE KEY type_descr = type_descr include_aliases = include_aliases level = level ASSIGNING <struct>. IF sy-subrc IS NOT INITIAL. struct_cache-type_descr = type_descr. struct_cache-include_aliases = include_aliases. struct_cache-level = level. CREATE DATA struct_cache-result-data TYPE HANDLE type_descr. INSERT struct_cache INTO TABLE mt_struct_cache ASSIGNING <struct>. ASSIGN <struct>-result-data->* TO <data>. comp_tab = type_descr->get_components( ). LOOP AT comp_tab ASSIGNING <comp>. IF <comp>-name IS NOT INITIAL AND ( <comp>-as_include EQ abap_false OR include_aliases EQ abap_true OR mv_expand_includes EQ abap_false ). symbol-name = <comp>-name. symbol-type = <comp>-type. IF symbol-type->kind EQ cl_abap_typedescr=>kind_elem. symbol-elem_type ?= symbol-type. ELSE. CLEAR symbol-elem_type. ENDIF. IF mv_conversion_exits EQ abap_true AND symbol-elem_type IS NOT INITIAL. symbol-convexit_in = get_convexit_func( elem_descr = symbol-elem_type input = abap_true ). symbol-convexit_out = get_convexit_func( elem_descr = symbol-elem_type input = abap_false ). ENDIF. is_compressable symbol-type symbol-name symbol-compressable. ASSIGN COMPONENT symbol-name OF STRUCTURE <data> TO <field>. GET REFERENCE OF <field> INTO symbol-value. format_name symbol-name mv_pretty_name symbol-header. CONCATENATE `"` symbol-header `":` INTO symbol-header. IF mv_format_output EQ abap_true. CONCATENATE symbol-header ` ` INTO symbol-header. ENDIF. APPEND symbol TO <struct>-result-symbols. ENDIF. IF <comp>-as_include EQ abap_true AND mv_expand_includes EQ abap_true. struct_descr ?= <comp>-type. sym_cache = get_symbols_struct( type_descr = struct_descr include_aliases = include_aliases ). LOOP AT sym_cache-symbols INTO symbol. CONCATENATE symbol-name <comp>-suffix INTO symbol-name. IF symbol-type->kind EQ cl_abap_typedescr=>kind_elem. symbol-elem_type ?= symbol-type. ELSE. CLEAR symbol-elem_type. ENDIF. IF mv_conversion_exits EQ abap_true AND symbol-elem_type IS NOT INITIAL. symbol-convexit_in = get_convexit_func( elem_descr = symbol-elem_type input = abap_true ). symbol-convexit_out = get_convexit_func( elem_descr = symbol-elem_type input = abap_false ). ENDIF. is_compressable symbol-type symbol-name symbol-compressable. ASSIGN COMPONENT symbol-name OF STRUCTURE <data> TO <field>. GET REFERENCE OF <field> INTO symbol-value. format_name symbol-name mv_pretty_name symbol-header. CONCATENATE `"` symbol-header `":` INTO symbol-header. IF mv_format_output EQ abap_true. CONCATENATE symbol-header ` ` INTO symbol-header. ENDIF. APPEND symbol TO <struct>-result-symbols. ENDLOOP. ENDIF. ENDLOOP. ENDIF. result = <struct>-result. IF data IS BOUND AND data NE <struct>-result-data. result-data = data. ASSIGN data->* TO <data>. LOOP AT result-symbols ASSIGNING <symbol>. ASSIGN COMPONENT <symbol>-name OF STRUCTURE <data> TO <field>. GET REFERENCE OF <field> INTO <symbol>-value. ENDLOOP. ENDIF. ENDMETHOD. "GET_SYMBOLS_STRUCT ENDCLASS.
1.内表或结构转JSON
- Json元素带双引号的转换
TYPES: BEGIN OF TY_DATA, MATNR TYPE MARA-MATNR, WERKS TYPE MARC-WERKS, END OF TY_DATA. DATA: WA_DATA TYPE TY_DATA, LT_DATA TYPE TABLE OF TY_DATA. DATA: JSON01 TYPE STRING, JSON02 TYPE STRING. WA_DATA-MATNR = '111'. WA_DATA-WERKS = '1001'. APPEND WA_DATA TO LT_DATA. WA_DATA-MATNR = '222'. WA_DATA-WERKS = '1001'. APPEND WA_DATA TO LT_DATA. "STRU->JSON JSON01 = /UI2/CL_JSON=>SERIALIZE( WA_DATA ). "ITAB->JSON JSON02 = /UI2/CL_JSON=>SERIALIZE( LT_DATA ). CL_DEMO_OUTPUT=>DISPLAY( JSON01 ). CL_DEMO_OUTPUT=>DISPLAY( JSON02 ).
2.JSON转内表或结构
- Json元素带双引号的转换
TYPES: BEGIN OF TY_DATA, MATNR TYPE MARA-MATNR, WERKS TYPE MARC-WERKS, END OF TY_DATA. DATA: WA_DATA TYPE TY_DATA, LT_DATA TYPE TABLE OF TY_DATA. DATA: JSON01 TYPE STRING, JSON02 TYPE STRING. JSON01 = '{"MATNR":"222","WERKS":"1001"}'. JSON02 = '[{"MATNR":"111","WERKS":"1001"},{"MATNR":"222","WERKS":"1001"}]'. "JSON->STRU /UI2/CL_JSON=>DESERIALIZE( EXPORTING JSON = JSON01 CHANGING DATA = WA_DATA ). "JSON->ITAB /UI2/CL_JSON=>DESERIALIZE( EXPORTING JSON = JSON02 CHANGING DATA = LT_DATA ). CL_DEMO_OUTPUT=>DISPLAY( WA_DATA ). CL_DEMO_OUTPUT=>DISPLAY( LT_DATA ).
3. 使用ZCL_JSON进行Json解析
- 解析方法和/UI2/JSON类似
- 可以INCLIUDE引入ZCL_JSON后直接使用
DATA: ls_mara1 TYPE mara, ls_mara2 TYPE mara. DATA: l_json TYPE string. ls_mara1-matnr = '1000003458'. l_json = zcl_json=>serialize( ls_mara1 ). "To Json zcl_json=>deserialize( EXPORTING json = l_json CHANGING data = ls_mara2 ). "deserializition json WRITE: / l_json,ls_mara2-matnr.

版权声明:本文为qq_30797051原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。