Skip to content

Commit

Permalink
Support xstring and string_table as input for parser (#190)
Browse files Browse the repository at this point in the history
* Support `xstring` and `string_table` as input

Closes #189

* lint
  • Loading branch information
mbtools authored Oct 21, 2024
1 parent d618c78 commit ac5f703
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 19 deletions.
2 changes: 1 addition & 1 deletion src/core/zcl_ajson.clas.abap
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class zcl_ajson definition

class-methods parse
importing
!iv_json type string
!iv_json type any
!iv_freeze type abap_bool default abap_false
!ii_custom_mapping type ref to zif_ajson_mapping optional
!iv_keep_item_order type abap_bool default abap_false
Expand Down
155 changes: 137 additions & 18 deletions src/core/zcl_ajson.clas.locals_imp.abap
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,27 @@ interface lif_kind.

constants:
begin of numeric,
int1 type ty_kind value cl_abap_tabledescr=>typekind_int1,
int2 type ty_kind value cl_abap_tabledescr=>typekind_int2,
int4 type ty_kind value cl_abap_tabledescr=>typekind_int,
int8 type ty_kind value '8', " cl_abap_tabledescr=>typekind_int8 not in lower releases
float type ty_kind value cl_abap_tabledescr=>typekind_float,
packed type ty_kind value cl_abap_tabledescr=>typekind_packed,
decfloat16 type ty_kind value cl_abap_tabledescr=>typekind_decfloat16,
decfloat34 type ty_kind value cl_abap_tabledescr=>typekind_decfloat34,
int1 type ty_kind value cl_abap_typedescr=>typekind_int1,
int2 type ty_kind value cl_abap_typedescr=>typekind_int2,
int4 type ty_kind value cl_abap_typedescr=>typekind_int,
int8 type ty_kind value '8', " cl_abap_typedescr=>typekind_int8 not in lower releases
float type ty_kind value cl_abap_typedescr=>typekind_float,
packed type ty_kind value cl_abap_typedescr=>typekind_packed,
decfloat16 type ty_kind value cl_abap_typedescr=>typekind_decfloat16,
decfloat34 type ty_kind value cl_abap_typedescr=>typekind_decfloat34,
end of numeric.

constants:
begin of texts,
char type ty_kind value cl_abap_tabledescr=>typekind_char,
numc type ty_kind value cl_abap_tabledescr=>typekind_num,
string type ty_kind value cl_abap_tabledescr=>typekind_string,
char type ty_kind value cl_abap_typedescr=>typekind_char,
numc type ty_kind value cl_abap_typedescr=>typekind_num,
string type ty_kind value cl_abap_typedescr=>typekind_string,
end of texts.

constants:
begin of binary,
hex type ty_kind value cl_abap_tabledescr=>typekind_hex,
xstring type ty_kind value cl_abap_tabledescr=>typekind_xstring,
hex type ty_kind value cl_abap_typedescr=>typekind_hex,
xstring type ty_kind value cl_abap_typedescr=>typekind_xstring,
end of binary.

constants:
Expand Down Expand Up @@ -80,6 +80,25 @@ class lcl_utils definition final.
iv_str type string
returning
value(rv_xstr) type xstring.
class-methods xstring_to_string_utf8
importing
iv_xstr type xstring
returning
value(rv_str) type string.
class-methods any_to_xstring
importing
iv_data type any
returning
value(rv_xstr) type xstring
raising
zcx_ajson_error.
class-methods any_to_string
importing
iv_data type any
returning
value(rv_str) type string
raising
zcx_ajson_error.

endclass.

Expand Down Expand Up @@ -116,6 +135,37 @@ class lcl_utils implementation.

endmethod.

method xstring_to_string_utf8.

data lo_conv type ref to object.
data lv_in_ce type string.

lv_in_ce = 'CL_ABAP_CONV_IN_CE'.

try.
call method ('CL_ABAP_CONV_CODEPAGE')=>create_in
receiving
instance = lo_conv.
call method lo_conv->('IF_ABAP_CONV_IN~CONVERT')
exporting
source = iv_xstr
receiving
result = rv_str.
catch cx_sy_dyn_call_illegal_class.
call method (lv_in_ce)=>create
exporting
encoding = 'UTF-8'
receiving
conv = lo_conv.
call method lo_conv->('CONVERT')
exporting
data = iv_xstr
importing
buffer = rv_str.
endtry.

endmethod.

method validate_array_index.

if not iv_index co '0123456789'.
Expand Down Expand Up @@ -169,6 +219,72 @@ class lcl_utils implementation.

endmethod.

method any_to_xstring.
" supports xstring, char, string, or string_table as input

data lo_type type ref to cl_abap_typedescr.
data lo_table_type type ref to cl_abap_tabledescr.
data lv_str type string.

field-symbols: <data> type standard table.

lo_type = cl_abap_typedescr=>describe_by_data( iv_data ).

case lo_type->type_kind.
when lif_kind=>binary-xstring.
rv_xstr = iv_data.
when lif_kind=>texts-string or lif_kind=>texts-char.
rv_xstr = string_to_xstring_utf8( iv_data ).
when lif_kind=>table.
lo_table_type ?= lo_type.
if lo_table_type->table_kind <> cl_abap_tabledescr=>tablekind_std.
zcx_ajson_error=>raise( 'Unsupported type of input table (must be standard table)' ).
endif.
try.
assign iv_data to <data>.
lv_str = concat_lines_of( table = <data> sep = cl_abap_char_utilities=>newline ).
rv_xstr = string_to_xstring_utf8( lv_str ).
catch cx_root.
zcx_ajson_error=>raise( 'Error converting input table (should be string_table)' ).
endtry.
when others.
zcx_ajson_error=>raise( 'Unsupported type of input (must be char, string, string_table, or xstring)' ).
endcase.

endmethod.

method any_to_string.
" supports xstring, char, string, or string_table as input

data lo_type type ref to cl_abap_typedescr.
data lo_table_type type ref to cl_abap_tabledescr.

field-symbols: <data> type standard table.

lo_type = cl_abap_typedescr=>describe_by_data( iv_data ).

case lo_type->type_kind.
when lif_kind=>binary-xstring.
rv_str = xstring_to_string_utf8( iv_data ).
when lif_kind=>texts-string or lif_kind=>texts-char.
rv_str = iv_data.
when lif_kind=>table.
lo_table_type ?= lo_type.
if lo_table_type->table_kind <> cl_abap_tabledescr=>tablekind_std.
zcx_ajson_error=>raise( 'Unsupported type of input table (must be standard table)' ).
endif.
try.
assign iv_data to <data>.
rv_str = concat_lines_of( table = <data> sep = cl_abap_char_utilities=>newline ).
catch cx_root.
zcx_ajson_error=>raise( 'Error converting input table (should be string_table)' ).
endtry.
when others.
zcx_ajson_error=>raise( 'Unsupported type of input (must be char, string, string_table, or xstring)' ).
endcase.

endmethod.

endclass.


Expand All @@ -181,7 +297,7 @@ class lcl_json_parser definition final.

methods parse
importing
iv_json type string
iv_json type any
iv_keep_item_order type abap_bool default abap_false
returning
value(rt_json_tree) type zif_ajson_types=>ty_nodes_tt
Expand All @@ -205,7 +321,7 @@ class lcl_json_parser definition final.

methods _parse
importing
iv_json type string
iv_json type xstring
returning
value(rt_json_tree) type zif_ajson_types=>ty_nodes_tt
raising
Expand All @@ -226,17 +342,20 @@ class lcl_json_parser implementation.
data lx_sxml_parse type ref to cx_sxml_parse_error.
data lx_sxml type ref to cx_dynamic_check.
data lv_location type string.
data lv_json type xstring.

mv_keep_item_order = iv_keep_item_order.

lv_json = lcl_utils=>any_to_xstring( iv_json ).

try.
" TODO sane JSON check:
" JSON can be true,false,null,(-)digits
" or start from " or from {
rt_json_tree = _parse( iv_json ).
rt_json_tree = _parse( lv_json ).
catch cx_sxml_parse_error into lx_sxml_parse.
lv_location = _get_location(
iv_json = iv_json
iv_json = lcl_utils=>any_to_string( iv_json )
iv_offset = lx_sxml_parse->xml_offset ).
zcx_ajson_error=>raise(
iv_msg = |Json parsing error (SXML): { lx_sxml_parse->get_text( ) }|
Expand Down Expand Up @@ -298,7 +417,7 @@ class lcl_json_parser implementation.
if iv_json is initial.
return.
endif.
lo_reader = cl_sxml_string_reader=>create( lcl_utils=>string_to_xstring_utf8( iv_json ) ).
lo_reader = cl_sxml_string_reader=>create( iv_json ).

" TODO: self protection, check non-empty, check starting from object ...

Expand Down
76 changes: 76 additions & 0 deletions src/core/zcl_ajson.clas.testclasses.abap
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ class ltcl_parser_test definition final
methods parse_date for testing raising zcx_ajson_error.
methods parse_bare_values for testing raising zcx_ajson_error.
methods parse_error for testing raising zcx_ajson_error.
methods parse_input_xstring for testing raising zcx_ajson_error.
methods parse_input_string for testing raising zcx_ajson_error.
methods parse_input_string_table for testing raising zcx_ajson_error.
methods parse_input_error for testing raising zcx_ajson_error.
methods duplicate_key for testing raising zcx_ajson_error.
methods non_json for testing raising zcx_ajson_error.

Expand Down Expand Up @@ -248,6 +252,78 @@ class ltcl_parser_test implementation.
exp = mo_nodes->mt_nodes ).
endmethod.

method parse_input_xstring.
mo_nodes->add( ' | |object | | |1' ).
mo_nodes->add( '/ |string |str |abc | |0' ).

data lt_act type zif_ajson_types=>ty_nodes_tt.
data lv_xstr type xstring.

lv_xstr = '7B22737472696E67223A2022616263227D0A'.
lt_act = mo_cut->parse( lv_xstr ).
cl_abap_unit_assert=>assert_equals(
act = lt_act
exp = mo_nodes->mt_nodes ).
endmethod.

method parse_input_string.
mo_nodes->add( ' | |object | | |1' ).
mo_nodes->add( '/ |string |str |abc | |0' ).

data lt_act type zif_ajson_types=>ty_nodes_tt.
data lv_str type string.

lv_str = `{"string": "abc"}`.
lt_act = mo_cut->parse( lv_str ).
cl_abap_unit_assert=>assert_equals(
act = lt_act
exp = mo_nodes->mt_nodes ).
endmethod.

method parse_input_string_table.
mo_nodes->add( ' | |object | | |2' ).
mo_nodes->add( '/ |string |str |abc | |0' ).
mo_nodes->add( '/ |number |num |123 | |0' ).

data lt_act type zif_ajson_types=>ty_nodes_tt.
data lt_json type string_table.

insert `{` into table lt_json.
insert `"string": "abc",` into table lt_json.
insert `"number": 123` into table lt_json.
insert `}` into table lt_json.

lt_act = mo_cut->parse( lt_json ).
cl_abap_unit_assert=>assert_equals(
act = lt_act
exp = mo_nodes->mt_nodes ).
endmethod.

method parse_input_error.

data lo_cut type ref to lcl_json_parser.
data lx type ref to zcx_ajson_error.
data lv_numc type n length 10.
data lt_hashed type hashed table of string with unique default key.

create object lo_cut.

try.
lo_cut->parse( lv_numc ).
cl_abap_unit_assert=>fail( ).
catch zcx_ajson_error into lx.
cl_abap_unit_assert=>assert_not_initial( lx ).
endtry.

try.
lo_cut->parse( lt_hashed ).
cl_abap_unit_assert=>fail( ).
catch zcx_ajson_error into lx.
cl_abap_unit_assert=>assert_not_initial( lx ).
endtry.

endmethod.

method sample_json.

rv_json =
Expand Down

0 comments on commit ac5f703

Please sign in to comment.