现在的系统没有安装相应的Add-on(UI2 Add-on,可以应用的版本是 SAP_BASIS 700 – 76X),因此找不到/UI2/CL_JSON类。因此创建了自定义类,用于替代原有类
1 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 15. "#EC NOTEXT
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 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
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
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
RETURNING
VALUE(rr_data) TYPE REF TO data
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 .
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_symbol,
header TYPE string,
name TYPE string,
type TYPE REF TO cl_abap_datadescr,
value TYPE REF TO data,
convexit_out TYPE string,
convexit_in TYPE string,
compressable TYPE abap_bool,
read_only TYPE abap_bool,
END OF t_s_symbol,
t_t_symbol TYPE STANDARD TABLE OF t_s_symbol WITH DEFAULT KEY,
BEGIN OF t_s_field_cache,
name TYPE string,
type TYPE REF TO cl_abap_datadescr,
convexit_out TYPE string,
convexit_in TYPE string,
value TYPE REF TO data,
END OF t_s_field_cache,
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.
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 mt_name_mappings TYPE name_mappings .
DATA mt_name_mappings_ex TYPE name_mappings_ex .
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_conversion_exits TYPE bool .
CLASS-DATA mc_name_symbols_map TYPE string VALUE ` _/_\_:_;_~_._,_-_+_=_>_<_|_(_)_[_]_{_}_@_+_*_?_!_&_$_#_%_^_'_§_` ##NO_TEXT.
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
RETURNING
VALUE(r_json) TYPE json .
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
RETURNING
VALUE(r_json) TYPE json .
METHODS is_compressable
IMPORTING
type_descr TYPE REF TO cl_abap_typedescr
name TYPE csequence
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 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
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 .
PRIVATE SECTION.
DATA mv_extended TYPE bool .
CLASS-DATA mc_me_type TYPE string .
CLASS-DATA mc_cov_error TYPE c .
ENDCLASS.
*----------------------------------------------------------------------*
* CLASS zcl_json MACROS
*----------------------------------------------------------------------*
DEFINE escape_json_inplace.
* replace all occurrences of regex `[\\"]` in &1 with `\\$0`. <-- this is slower than 2 plain replaces
REPLACE ALL OCCURRENCES OF `\` IN &1 WITH `\\`.
REPLACE ALL OCCURRENCES OF `"` IN &1 WITH `\"`.
END-OF-DEFINITION.
DEFINE escape_json.
&2 = &1.
escape_json_inplace &2.
END-OF-DEFINITION.
DEFINE is_compressable.
IF 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 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 = `""`.
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.
&3 = xstring_to_string( &1 ).
escape_json_inplace &3.
CONCATENATE `"` &3 `"` INTO &3.
ENDIF.
WHEN cl_abap_typedescr=>typekind_char.
IF &2->output_length EQ 1 AND mc_bool_types CS &2->absolute_name.
IF &1 EQ c_bool-true.
&3 = `true`. "#EC NOTEXT
ELSEIF mc_bool_3state CS &2->absolute_name AND &1 IS INITIAL.
&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.
CONCATENATE `"` &1(4) `-` &1+4(2) `-` &1+6(2) `"` INTO &3.
WHEN cl_abap_typedescr=>typekind_time.
CONCATENATE `"` &1(2) `:` &1+2(2) `:` &1+4(2) `"` INTO &3.
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 throw_error.
RAISE EXCEPTION TYPE cx_sy_move_cast_error.
END-OF-DEFINITION.
DEFINE while_offset_cs.
* >= 7.02 alternative
* pos = find_any_not_of( val = json sub = &1 off = offset ).
* if pos eq -1. offset = length.
* else. offset = pos. endif.
* < 7.02
WHILE offset < length.
FIND FIRST OCCURRENCE OF json+offset(1) IN &1.
IF sy-subrc IS NOT INITIAL.
EXIT.
ENDIF.
offset = offset + 1.
ENDWHILE.
* < 7.02
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.
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.
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 mc_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' ).
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.
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.
" **********************************************************************
" Usage examples and documentation can be found on SCN:
" http://wiki.scn.sap.com/wiki/display/Snippets/One+more+ABAP+to+JSON+Serializer+and+Deserializer
" ********************************************************************** "
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
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.
" **********************************************************************
" Usage examples and documentation can be found on SCN:
" http://wiki.scn.sap.com/wiki/display/Snippets/One+more+ABAP+to+JSON+Serializer+and+Deserializer
" ********************************************************************** "
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.
unescaped = unescaped+offset.
length = length - offset.
restore_type( EXPORTING json = unescaped length = length CHANGING data = data ).
ENDIF.
ENDMETHOD. "deserialize
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,
lt_symbols TYPE t_t_symbol,
lt_keys LIKE lt_symbols,
lt_properties TYPE STANDARD TABLE OF string,
lt_fields 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,
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> LIKE LINE OF lt_symbols,
<table> TYPE ANY TABLE.
" 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 ).
ELSE.
lo_obj_ref ?= data.
lo_classdesc ?= cl_abap_typedescr=>describe_by_object_ref( lo_obj_ref ).
lt_symbols = get_symbols( type_descr = lo_classdesc object = lo_obj_ref ).
r_json = dump_symbols( lt_symbols ).
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.
GET REFERENCE OF data INTO lo_data_ref.
lt_symbols = get_symbols( type_descr = lo_structdesc data = lo_data_ref ).
r_json = dump_symbols( lt_symbols ).
WHEN cl_abap_typedescr=>kind_table.
lo_tabledescr ?= type_descr.
lo_typedesc = lo_tabledescr->get_table_line_type( ).
ASSIGN data TO <table>.
" optimization for structured tables
IF lo_typedesc->kind EQ cl_abap_typedescr=>kind_struct.
lo_structdesc ?= lo_typedesc.
CREATE DATA lo_data_ref LIKE LINE OF <table>.
ASSIGN lo_data_ref->* TO <line>.
lt_symbols = get_symbols( type_descr = lo_structdesc data = lo_data_ref ).
" 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 lt_symbols WITH KEY name = <key>-name ASSIGNING <symbol>.
APPEND <symbol> TO lt_keys.
ENDLOOP.
ENDIF.
IF lines( lo_tabledescr->key ) EQ 1.
READ TABLE lo_tabledescr->key INDEX 1 INTO ls_skip_key.
DELETE lt_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( lt_symbols ) EQ 1.
lv_array_opt = abap_true.
ENDIF.
ENDIF.
LOOP AT <table> INTO <line>.
CLEAR: lt_fields, lv_prop_name.
LOOP AT lt_symbols ASSIGNING <symbol>.
ASSIGN <symbol>-value->* TO <value>.
IF mv_compress IS INITIAL OR <value> IS NOT INITIAL OR <symbol>-compressable EQ abap_false.
IF <symbol>-type->kind EQ cl_abap_typedescr=>kind_elem.
lo_elem_descr ?= <symbol>-type.
dump_type <value> lo_elem_descr lv_itemval <symbol>-convexit_out.
ELSE.
lv_itemval = dump_int( data = <value> type_descr = <symbol>-type convexit = <symbol>-convexit_out ).
ENDIF.
IF lv_array_opt EQ abap_false.
CONCATENATE <symbol>-header lv_itemval INTO lv_itemval.
ENDIF.
APPEND lv_itemval TO lt_fields.
ENDIF.
ENDLOOP.
IF lo_tabledescr->key_defkind EQ lo_tabledescr->keydefkind_user.
LOOP AT lt_keys 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.
ELSE.
LOOP AT lt_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.
CONCATENATE LINES OF lt_fields INTO lv_itemval SEPARATED BY `,`.
IF lv_array_opt EQ abap_false.
CONCATENATE `"` lv_prop_name `":{` lv_itemval `}` INTO lv_itemval.
ELSE.
CONCATENATE `"` lv_prop_name `":` lv_itemval `` INTO lv_itemval.
ENDIF.
APPEND lv_itemval TO lt_properties.
ENDLOOP.
CONCATENATE LINES OF lt_properties INTO r_json SEPARATED BY `,`.
CONCATENATE `{` r_json `}` INTO r_json.
ELSE.
LOOP AT <table> INTO <line>.
CLEAR lt_fields.
LOOP AT lt_symbols ASSIGNING <symbol>.
ASSIGN <symbol>-value->* TO <value>.
IF mv_compress IS INITIAL OR <value> IS NOT INITIAL OR <symbol>-compressable EQ abap_false.
IF <symbol>-type->kind EQ cl_abap_typedescr=>kind_elem.
lo_elem_descr ?= <symbol>-type.
dump_type <value> lo_elem_descr lv_itemval <symbol>-convexit_out.
ELSE.
lv_itemval = dump_int( data = <value> type_descr = <symbol>-type convexit = <symbol>-convexit_out ).
ENDIF.
CONCATENATE <symbol>-header lv_itemval INTO lv_itemval.
APPEND lv_itemval TO lt_fields.
ENDIF.
ENDLOOP.
CONCATENATE LINES OF lt_fields INTO lv_itemval SEPARATED BY `,`.
CONCATENATE `{` lv_itemval `}` INTO lv_itemval.
APPEND lv_itemval TO lt_properties.
ENDLOOP.
CONCATENATE LINES OF lt_properties INTO r_json SEPARATED BY `,`.
CONCATENATE `[` r_json `]` INTO r_json.
ENDIF.
ELSE.
LOOP AT <table> ASSIGNING <value>.
lv_itemval = dump_int( data = <value> type_descr = lo_typedesc ).
APPEND lv_itemval TO lt_properties.
ENDLOOP.
CONCATENATE LINES OF lt_properties INTO r_json SEPARATED BY `,`.
CONCATENATE `[` r_json `]` INTO r_json.
ENDIF.
ENDCASE.
ENDMETHOD. "dump
METHOD dump_symbols.
DATA: lv_properties TYPE STANDARD TABLE OF string,
lv_itemval TYPE string.
FIELD-SYMBOLS: <value> TYPE any,
<symbol> LIKE LINE OF it_symbols.
LOOP AT it_symbols ASSIGNING <symbol>.
ASSIGN <symbol>-value->* TO <value>.
IF mv_compress IS INITIAL OR <value> IS NOT INITIAL OR <symbol>-compressable EQ abap_false.
lv_itemval = dump_int( data = <value> type_descr = <symbol>-type convexit = <symbol>-convexit_out ).
CONCATENATE <symbol>-header lv_itemval INTO lv_itemval.
APPEND lv_itemval TO lv_properties.
ENDIF.
ENDLOOP.
CONCATENATE LINES OF lv_properties INTO r_json SEPARATED BY `,`.
CONCATENATE `{` r_json `}` INTO r_json.
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 = `""`.
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.
r_json = xstring_to_string( data ).
r_json = escape( r_json ).
CONCATENATE `"` r_json `"` INTO r_json.
ENDIF.
WHEN cl_abap_typedescr=>typekind_char.
IF type_descr->output_length EQ 1 AND mc_bool_types CS type_descr->absolute_name.
IF data EQ c_bool-true.
r_json = `true`. "#EC NOTEXT
ELSEIF mc_bool_3state CS type_descr->absolute_name AND data IS INITIAL.
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.
CONCATENATE `"` data(4) `-` data+4(2) `-` data+6(2) `"` INTO r_json.
WHEN cl_abap_typedescr=>typekind_time.
CONCATENATE `"` data(2) `:` data+2(2) `:` data+4(2) `"` INTO r_json.
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 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.
out = in.
REPLACE ALL OCCURRENCES OF `\` IN out WITH `\\`.
REPLACE ALL OCCURRENCES OF `"` IN out WITH `\"`.
ENDMETHOD. "escape
METHOD generate.
DATA: lo_json TYPE REF TO zcl_json,
offset TYPE i,
length TYPE i,
lv_json LIKE json.
" skip leading BOM signs
length = strlen( json ).
while_offset_not_cs `"{[` json.
lv_json = json+offset.
length = length - offset.
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 .
rr_data = lo_json->generate_int( json = lv_json length = length ).
CATCH cx_sy_move_cast_error. "#EC NO_HANDLER
ENDTRY.
ENDMETHOD.
METHOD generate_int.
TYPES: BEGIN OF ts_field,
name TYPE string,
value TYPE json,
END OF ts_field.
DATA: offset TYPE i.
DATA: lt_json TYPE STANDARD TABLE OF json WITH DEFAULT KEY,
lv_comp_name TYPE abap_compname,
lt_fields TYPE SORTED TABLE OF ts_field WITH UNIQUE KEY name,
lo_type TYPE REF TO cl_abap_datadescr,
lt_comp TYPE abap_component_tab,
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: <data> TYPE any,
<struct> TYPE any,
<json> LIKE LINE OF lt_json,
<field> LIKE LINE OF lt_fields,
<table> TYPE STANDARD TABLE,
<cache> LIKE LINE OF mt_name_mappings_ex.
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 CHANGING data = lt_fields ).
IF lt_fields IS NOT INITIAL.
ls_comp-type = cl_abap_refdescr=>get_ref_to_data( ).
LOOP AT lt_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 lt_fields.
ENDIF.
ENDLOOP.
TRY.
lo_type = cl_abap_structdescr=>create( p_components = lt_comp p_strict = c_bool-false ).
CREATE DATA rr_data TYPE HANDLE lo_type.
ASSIGN rr_data->* TO <struct>.
LOOP AT lt_fields ASSIGNING <field>.
ASSIGN COMPONENT sy-tabix OF STRUCTURE <struct> TO <data>.
<data> = generate_int( <field>-value ).
ENDLOOP.
CATCH cx_sy_create_data_error cx_sy_struct_creation. "#EC NO_HANDLER
ENDTRY.
ENDIF.
RETURN.
WHEN `[`."result must be a table of ref
restore_type( EXPORTING json = json length = length CHANGING data = lt_json ).
CREATE DATA rr_data TYPE ref_tab.
ASSIGN rr_data->* TO <table>.
LOOP AT lt_json ASSIGNING <json>.
APPEND INITIAL LINE TO <table> ASSIGNING <data>.
<data> = generate_int( <json> ).
ENDLOOP.
RETURN.
WHEN `"`."string
CREATE DATA rr_data TYPE string.
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 '.'.
CREATE DATA rr_data TYPE f.
ELSEIF length GT 9.
CREATE DATA rr_data TYPE p.
ELSE.
CREATE DATA rr_data TYPE i.
ENDIF.
WHEN OTHERS.
IF json+offset EQ `true` OR json+offset EQ `false`. "#EC NOTEXT
CREATE DATA rr_data TYPE abap_bool.
ENDIF.
ENDCASE.
IF rr_data IS BOUND.
ASSIGN rr_data->* TO <data>.
restore_type( EXPORTING json = json length = length CHANGING data = <data> ).
ENDIF.
ENDMETHOD.
METHOD generate_int_ex.
DATA: lv_assoc_arrays LIKE mv_assoc_arrays,
lv_assoc_arrays_opt LIKE mv_assoc_arrays_opt,
lv_mark LIKE offset,
lv_match LIKE lv_mark,
lv_json TYPE zcl_json=>json.
lv_mark = offset.
restore_type( EXPORTING json = json length = length CHANGING offset = offset ).
lv_match = offset - lv_mark.
lv_json = json+lv_mark(lv_match).
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.
data = generate_int( lv_json ).
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: comp_tab TYPE cl_abap_structdescr=>component_table,
symb_tab LIKE result,
symb LIKE LINE OF symb_tab,
elem_descr TYPE REF TO cl_abap_elemdescr,
class_descr TYPE REF TO cl_abap_classdescr,
struct_descr TYPE REF TO cl_abap_structdescr.
FIELD-SYMBOLS: <comp> LIKE LINE OF comp_tab,
<attr> LIKE LINE OF cl_abap_objectdescr=>attributes,
<cache> LIKE LINE OF mt_name_mappings,
<field> TYPE any.
IF type_descr->kind EQ cl_abap_typedescr=>kind_struct.
struct_descr ?= type_descr.
comp_tab = struct_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 ).
symb-name = <comp>-name.
symb-type = <comp>-type.
IF data IS BOUND.
IF mv_conversion_exits EQ abap_true AND symb-type->kind EQ cl_abap_typedescr=>kind_elem.
elem_descr ?= symb-type.
symb-convexit_in = get_convexit_func( elem_descr = elem_descr input = abap_true ).
symb-convexit_out = get_convexit_func( elem_descr = elem_descr input = abap_false ).
ENDIF.
is_compressable symb-type symb-name symb-compressable.
ASSIGN data->(symb-name) TO <field>.
GET REFERENCE OF <field> INTO symb-value.
format_name symb-name mv_pretty_name symb-header.
CONCATENATE `"` symb-header `":` INTO symb-header.
ENDIF.
APPEND symb TO result.
ENDIF.
IF <comp>-as_include EQ abap_true AND mv_expand_includes EQ abap_true.
struct_descr ?= <comp>-type.
symb_tab = get_symbols( type_descr = struct_descr include_aliases = include_aliases ).
LOOP AT symb_tab INTO symb.
CONCATENATE symb-name <comp>-suffix INTO symb-name.
IF data IS BOUND.
IF mv_conversion_exits EQ abap_true AND symb-type->kind EQ cl_abap_typedescr=>kind_elem.
elem_descr ?= symb-type.
symb-convexit_in = get_convexit_func( elem_descr = elem_descr input = abap_true ).
symb-convexit_out = get_convexit_func( elem_descr = elem_descr input = abap_false ).
ENDIF.
is_compressable symb-type symb-name symb-compressable.
ASSIGN data->(symb-name) TO <field>.
GET REFERENCE OF <field> INTO symb-value.
format_name symb-name mv_pretty_name symb-header.
CONCATENATE `"` symb-header `":` INTO symb-header.
ENDIF.
APPEND symb TO result.
ENDLOOP.
ENDIF.
ENDLOOP.
ELSEIF type_descr->type_kind EQ cl_abap_typedescr=>typekind_class.
class_descr ?= type_descr.
LOOP AT class_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 = class_descr->get_attribute_type( <attr>-name ).
IF mv_conversion_exits EQ abap_true AND symb-type->kind EQ cl_abap_typedescr=>kind_elem.
elem_descr ?= symb-type.
symb-convexit_in = get_convexit_func( elem_descr = elem_descr input = abap_true ).
symb-convexit_out = get_convexit_func( elem_descr = elem_descr 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.
APPEND symb TO result.
ENDLOOP.
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 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).
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.
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,
lt_symbols TYPE t_t_symbol,
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,
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> LIKE LINE OF lt_symbols.
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 type_descr IS NOT INITIAL 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).
ENDIF.
CASE json+offset(1).
WHEN `{`. " object
IF type_descr IS NOT INITIAL.
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.
lt_symbols = get_symbols( type_descr = data_descr data = line ).
DELETE lt_symbols WHERE name EQ key_name.
IF lines( lt_symbols ) EQ 1.
READ TABLE lt_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 type_descr IS NOT INITIAL 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 type_descr IS NOT INITIAL 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 type_descr IS NOT INITIAL.
eat_char `]`.
throw_error.
ENDIF.
ENDIF.
ELSE.
CLEAR data.
ENDIF.
eat_char `]`.
ENDIF.
WHEN `"`. " string
eat_string sdummy.
IF type_descr IS NOT INITIAL.
" 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 mc_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( EXPORTING in = sdummy CHANGING out = 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( EXPORTING in = sdummy CHANGING out = 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 type_descr IS NOT INITIAL.
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 type_descr IS NOT INITIAL.
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.
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.
" **********************************************************************
" Usage examples and documentation can be found on SCN:
" http://wiki.scn.sap.com/wiki/display/Snippets/One+more+ABAP+to+JSON+Serializer+and+Deserializer
" ********************************************************************** "
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
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.
" **********************************************************************
" Usage examples and documentation can be found on SCN:
" http://wiki.scn.sap.com/wiki/display/Snippets/One+more+ABAP+to+JSON+Serializer+and+Deserializer
" ********************************************************************** "
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 ).
" we do not do escaping of every single string value for white space characters,
" but we do it on top, to replace multiple calls by 3 only, while we do not serialize
" outlined/formatted JSON this shall not produce any harm
REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>cr_lf IN r_json WITH `\r\n`.
REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>newline IN r_json WITH `\n`.
REPLACE ALL OCCURRENCES OF cl_abap_char_utilities=>horizontal_tab IN r_json WITH `\t`.
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 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 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 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 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.
ENDCLASS.
2 例子
REPORT zlyf_001 .
INCLUDE zlyf_001_1 . "zcl_json类的源码
TYPES: BEGIN OF t_record,
key1 TYPE string,
key2 TYPE i,
END OF t_record.
DATA: l_json TYPE string VALUE '{"key1": "VALUE1", "key2": "2"}'.
DATA: record TYPE t_record.
DATA: lt_record TYPE TABLE OF t_record.
START-OF-SELECTION .
"json转abap内表
zcl_json=>deserialize( EXPORTING json = l_json CHANGING data = record ).
WRITE : / record-key1 , record-key2 .
APPEND record TO lt_record .
"abap内表 转 json
CLEAR l_json.
l_json = zcl_json=>serialize( lt_record ).
WRITE : / l_json .