SAP内表转json json转内表方法(自定义实现 ZCL_JSON)

=> Reference

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

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

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.
    
    4
    5
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值