Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Added some module tests
REPORT ZZ_TEST_JSON_MYDOG.
*
class lcl_test definition for testing " #AU Risk_Level Harmless
inheriting from cl_aunit_assert. " #AU Duration Short
private section.
methods :
test_string for testing,
test_number for testing,
test_bool_true for testing, " <---- fails
test_bool_false for testing, " <---- fails
test_array_of_strings for testing,
test_hash_of_numbers for testing,
test_deep for testing.
endclass.
*
class lcl_test implementation.
*
method test_string.
data: lv_string type string.
perform json_to_data using 'Abc - Def' changing lv_string.
assert_equals( act = lv_string exp = 'Abc - Def' ).
endmethod.
method test_number.
data: lv_number type i.
perform json_to_data using '123456' changing lv_number.
assert_equals( act = lv_number exp = 123456 ).
endmethod.
method test_bool_true.
data: lv_flag type flag.
perform json_to_data using 'true' changing lv_flag.
assert_equals( act = lv_flag exp = 'X' ).
endmethod.
method test_bool_false.
data: lv_flag type flag.
perform json_to_data using 'false' changing lv_flag.
assert_equals( act = lv_flag exp = space ).
endmethod.
method test_array_of_strings.
data: lt_strings type stringtab,
lt_exp type stringtab.
append: 'a' to lt_exp, 'b' to lt_exp, 'c' to lt_exp.
perform json_to_data using '["a","b","c"]' changing lt_strings.
assert_equals( act = lt_strings exp = lt_exp ).
endmethod.
method test_hash_of_numbers.
types: begin of ty_hash,
a type i,
b type i,
c type i,
end of ty_hash.
data: ls_hash type ty_hash,
ls_exp type ty_hash.
ls_exp-a = 1.
ls_exp-b = 2.
ls_exp-c = 3.
perform json_to_data using '{"a":1,"b":2,"c":3}' changing ls_hash.
assert_equals( act = ls_hash exp = ls_exp ).
endmethod.
method test_deep.
types: begin of ty_hash,
a type i,
b type i,
c type i,
d type stringtab,
end of ty_hash,
ty_hash_tab type standard table of ty_hash.
data: ls_deep type ty_hash,
ls_exp type ty_hash,
lt_deep type ty_hash_tab,
lt_exp type ty_hash_tab.
clear ls_exp.
ls_exp-a = 1.
ls_exp-b = 2.
ls_exp-c = 3.
append 'e' to ls_exp-d.
append 'f' to ls_exp-d.
append 'g' to ls_exp-d.
append ls_exp to lt_exp.
clear ls_exp.
ls_exp-a = 4.
ls_exp-b = 5.
ls_exp-c = -6.
append 'h' to ls_exp-d.
append 'i' to ls_exp-d.
append ls_exp to lt_exp.
perform json_to_data using
'[{"a":1,"b":2,"c":3,"d":["e","f","g"]} , ' &
' {"a":4,"b":5,"c":-6,"d":["h","i"]} ]'
changing lt_deep.
assert_equals( act = lt_deep exp = lt_exp ).
endmethod.
endclass.
*&---------------------------------------------------------------------*
*& Form json_to_data
*&---------------------------------------------------------------------*
form json_to_data using json type string changing data type any.
data: lv_off type i.
data: lv_len type i.
data: lv_key type string.
data: lv_value type string.
data: lv_char type char1. "Current chacater
data: lv_pchar type char1. "Previous character
data: lv_instr type boole_d. "Indicator: cursor in string
data: lv_level type i. "Depth inside a block
data: lv_table type boole_d.
field-symbols: <fk> type string.
field-symbols: <data> type any.
field-symbols: <table> type any table.
field-symbols: <sotab> type sorted table.
field-symbols: <sttab> type standard table.
field-symbols: <line> type any.
data: ls_line type ref to data.
data: lr_td type ref to cl_abap_typedescr.
data: lr_ttd type ref to cl_abap_tabledescr.
* If the incoming json contains no '{}[]', we are dealing with
* a basic (scalar) value that is simply assigned to the data
* and then we return
if json na '{}[]'.
* Replace escape characters (TODO: Check if there are more!)
lv_value = json.
replace all occurrences of '\n' in lv_value with cl_abap_char_utilities=>newline.
replace all occurrences of '\t' in lv_value with cl_abap_char_utilities=>horizontal_tab.
replace all occurrences of '\f' in lv_value with cl_abap_char_utilities=>form_feed.
replace all occurrences of '\v' in lv_value with cl_abap_char_utilities=>vertical_tab.
replace all occurrences of '\b' in lv_value with cl_abap_char_utilities=>backspace.
replace all occurrences of '\\' in lv_value with '\'.
* TODO: Deal with specific data types, e.g. dates etc.
data = lv_value.
exit.
endif.
lv_len = strlen( json ).
* Check if we are dealing with a table
lr_td = cl_abap_typedescr=>describe_by_data( data ).
if lr_td->type_kind = cl_abap_typedescr=>typekind_table.
* This information is used later...
lv_table = 'X'.
assign data to <table>.
create data ls_line like line of <table>.
assign ls_line->* to <line>.
else.
lv_table = ' '.
endif.
* Reset counters/flags
lv_off = 0.
lv_instr = ' '.
lv_level = 0.
assign lv_key to <fk>.
while lv_off < lv_len.
lv_char = json+lv_off(1).
**********************************************************************
* IN STRING
**********************************************************************
* Character is in a string delimited by double quotes
if lv_instr = 'X'.
if lv_char = '"'.
* Switch out of delimited character string
if lv_pchar ne '\'.
lv_instr = ' '.
else.
concatenate <fk> lv_char into <fk> respecting blanks.
endif.
else.
concatenate <fk> lv_char into <fk> respecting blanks.
endif.
**********************************************************************
* OUTSIDE STRING
**********************************************************************
* Character is not in a string delimited by double quotes
else.
* On opening character, shift level up
if lv_char ca '{['.
add 1 to lv_level.
endif.
* When the value is contained in a {}/[], the entire value must
* be passed to the next level of processing
if lv_level > 1.
concatenate <fk> lv_char into <fk> respecting blanks.
else.
if lv_char ca '[{'. "<- Chars ignored outside of str
elseif lv_char = ':'.
assign lv_value to <fk>.
* The key collected up to now is assigned to the data member
translate lv_key to upper case.
shift lv_key left deleting leading space.
assign component lv_key of structure data to <data>.
* End of a key/value pair (we bump up against delimiter) - pass to next level
elseif ( lv_char = ',' or lv_char = '}' ) and lv_table = space.
* Process collected value
shift lv_value left deleting leading space.
perform json_to_data using lv_value changing <data>.
* Clear key and value
clear: lv_key, lv_value.
assign lv_key to <fk>.
clear: lv_key, lv_value.
* End of a key/value pair (we bump up against delimiter) - pass to next level
* But in table mode, we pass an instance of a row of the table, and afterward
* add it to the table
elseif ( lv_char = ',' or lv_char = ']' ) and lv_table = 'X'.
* Process collected value
* Inside array in JSON, there are no keys, only list of values, the collected
* value is in lv_key
shift lv_key left deleting leading space.
perform json_to_data using lv_key changing <line>.
* On return: if dealing with table, add the record to the table
lr_ttd ?= lr_td.
if lr_ttd->table_kind = cl_abap_tabledescr=>tablekind_sorted
or lr_ttd->table_kind = cl_abap_tabledescr=>tablekind_hashed.
assign data to <sotab>.
insert <line> into table <sotab>.
else.
assign data to <sttab>.
append <line> to <sttab>.
endif.
clear <line>.
* Clear key and value
clear: lv_key, lv_value.
assign lv_key to <fk>.
clear: lv_key, lv_value.
* Switch cursor into delimited string; consecutive characters
* are then treated as part of a key or value, even if they are
* special characters
elseif lv_char = '"'.
lv_instr = 'X'.
* Other chars processed as either key or value
else.
concatenate <fk> lv_char into <fk> respecting blanks.
endif.
endif.
* On closing character, shift level down again
if lv_char ca '}]'.
subtract 1 from lv_level.
endif.
* END: Are we in string or out?
endif.
lv_pchar = lv_char.
add 1 to lv_off.
endwhile.
endform. "json_to_data
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment