From b10276ffa5f0297f5247c512862ec5639f9b37e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 09:09:50 +0200 Subject: [PATCH] ajson, Automatic Update (#6341) Co-authored-by: larshp Co-authored-by: Lars Hvam --- src/json/zcl_abapgit_ajson.clas.abap | 27 +++-- .../zcl_abapgit_ajson.clas.locals_imp.abap | 12 +++ .../zcl_abapgit_ajson.clas.testclasses.abap | 100 ++++++++++++++++++ 3 files changed, 131 insertions(+), 8 deletions(-) diff --git a/src/json/zcl_abapgit_ajson.clas.abap b/src/json/zcl_abapgit_ajson.clas.abap index 190aaaaed..ec1bfb80e 100644 --- a/src/json/zcl_abapgit_ajson.clas.abap +++ b/src/json/zcl_abapgit_ajson.clas.abap @@ -50,9 +50,10 @@ CLASS zcl_abapgit_ajson DEFINITION CLASS-METHODS parse IMPORTING - !iv_json TYPE string - !iv_freeze TYPE abap_bool DEFAULT abap_false - !ii_custom_mapping TYPE REF TO zif_abapgit_ajson_mapping OPTIONAL + !iv_json TYPE string + !iv_freeze TYPE abap_bool DEFAULT abap_false + !ii_custom_mapping TYPE REF TO zif_abapgit_ajson_mapping OPTIONAL + !iv_keep_item_order TYPE abap_bool DEFAULT abap_false RETURNING VALUE(ro_instance) TYPE REF TO zcl_abapgit_ajson RAISING @@ -247,8 +248,11 @@ CLASS zcl_abapgit_ajson IMPLEMENTATION. CREATE OBJECT ro_instance. CREATE OBJECT lo_parser. - ro_instance->mt_json_tree = lo_parser->parse( iv_json ). + ro_instance->mt_json_tree = lo_parser->parse( + iv_json = iv_json + iv_keep_item_order = iv_keep_item_order ). ro_instance->mi_custom_mapping = ii_custom_mapping. + ro_instance->ms_opts-keep_item_order = iv_keep_item_order. IF iv_freeze = abap_true. ro_instance->freeze( ). @@ -592,6 +596,7 @@ CLASS zcl_abapgit_ajson IMPLEMENTATION. DATA ls_split_path TYPE zif_abapgit_ajson_types=>ty_path_name. DATA lr_parent TYPE REF TO zif_abapgit_ajson_types=>ty_node. DATA ls_deleted_node TYPE zif_abapgit_ajson_types=>ty_node. + DATA lv_item_order TYPE zif_abapgit_ajson_types=>ty_node-order. read_only_watchdog( ). @@ -635,6 +640,7 @@ CLASS zcl_abapgit_ajson IMPLEMENTATION. ir_parent = lr_parent iv_path = ls_split_path-path iv_name = ls_split_path-name ). + lv_item_order = ls_deleted_node-order. " convert to json DATA lt_new_nodes TYPE zif_abapgit_ajson_types=>ty_nodes_tt. @@ -644,12 +650,15 @@ CLASS zcl_abapgit_ajson IMPLEMENTATION. lv_array_index = lcl_utils=>validate_array_index( iv_path = ls_split_path-path iv_index = ls_split_path-name ). + ELSEIF lr_parent->type = zif_abapgit_ajson_types=>node_type-object + AND lv_item_order = 0 AND ms_opts-keep_item_order = abap_true. + lv_item_order = lr_parent->children + 1. ENDIF. IF iv_node_type IS NOT INITIAL. lt_new_nodes = lcl_abap_to_json=>insert_with_type( is_opts = ms_opts - iv_item_order = ls_deleted_node-order + iv_item_order = lv_item_order iv_data = iv_val iv_type = iv_node_type iv_array_index = lv_array_index @@ -658,7 +667,7 @@ CLASS zcl_abapgit_ajson IMPLEMENTATION. ELSE. lt_new_nodes = lcl_abap_to_json=>convert( is_opts = ms_opts - iv_item_order = ls_deleted_node-order + iv_item_order = lv_item_order iv_data = iv_val iv_array_index = lv_array_index is_prefix = ls_split_path @@ -725,7 +734,9 @@ CLASS zcl_abapgit_ajson IMPLEMENTATION. "Expect object/array, but no further checks, parser will catch errors zif_abapgit_ajson~set( iv_path = lv_path - iv_val = parse( lv_val ) ). + iv_val = parse( + iv_json = lv_val + iv_keep_item_order = ms_opts-keep_item_order ) ). ELSE. " string lv_last = strlen( lv_val ) - 1. IF lv_val+0(1) = '"' AND lv_val+lv_last(1) = '"'. @@ -847,7 +858,7 @@ CLASS zcl_abapgit_ajson IMPLEMENTATION. RETURN. ENDIF. - CLEAR: ls_item-path, ls_item-name. " this becomes a new root + CLEAR: ls_item-path, ls_item-name, ls_item-order. " this becomes a new root INSERT ls_item INTO TABLE lo_section->mt_json_tree. lv_path_pattern = lv_normalized_path && `*`. diff --git a/src/json/zcl_abapgit_ajson.clas.locals_imp.abap b/src/json/zcl_abapgit_ajson.clas.locals_imp.abap index 4c152ec29..b018968ec 100644 --- a/src/json/zcl_abapgit_ajson.clas.locals_imp.abap +++ b/src/json/zcl_abapgit_ajson.clas.locals_imp.abap @@ -189,6 +189,7 @@ CLASS lcl_json_parser DEFINITION FINAL. METHODS parse IMPORTING iv_json TYPE string + iv_keep_item_order TYPE abap_bool DEFAULT abap_false RETURNING VALUE(rt_json_tree) TYPE zif_abapgit_ajson_types=>ty_nodes_tt RAISING @@ -201,6 +202,7 @@ CLASS lcl_json_parser DEFINITION FINAL. DATA mt_stack TYPE ty_stack_tt. DATA mv_stack_path TYPE string. + DATA mv_keep_item_order TYPE abap_bool. METHODS raise IMPORTING @@ -231,6 +233,9 @@ 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. + + mv_keep_item_order = iv_keep_item_order. + TRY. " TODO sane JSON check: " JSON can be true,false,null,(-)digits @@ -248,6 +253,7 @@ CLASS lcl_json_parser IMPLEMENTATION. iv_msg = |Json parsing error (SXML): { lx_sxml->get_text( ) }| iv_location = '@PARSER' ). ENDTRY. + ENDMETHOD. METHOD _get_location. @@ -337,6 +343,9 @@ CLASS lcl_json_parser IMPLEMENTATION. -name = lo_attr->get_value( ). ENDIF. ENDLOOP. + IF mv_keep_item_order = abap_true. + -order = lr_stack_top->children. + ENDIF. ENDIF. IF -name IS INITIAL. raise( 'Node without name (maybe not JSON)' ). @@ -1164,6 +1173,7 @@ CLASS lcl_abap_to_json DEFINITION FINAL. io_json TYPE REF TO zif_abapgit_ajson is_prefix TYPE zif_abapgit_ajson_types=>ty_path_name iv_index TYPE i DEFAULT 0 + iv_item_order TYPE i DEFAULT 0 CHANGING ct_nodes TYPE zif_abapgit_ajson_types=>ty_nodes_tt RAISING @@ -1324,6 +1334,7 @@ CLASS lcl_abap_to_json IMPLEMENTATION. io_json = iv_data is_prefix = is_prefix iv_index = iv_index + iv_item_order = iv_item_order CHANGING ct_nodes = ct_nodes ). ELSE. @@ -1351,6 +1362,7 @@ CLASS lcl_abap_to_json IMPLEMENTATION. -path = is_prefix-path. -name = is_prefix-name. -index = iv_index. + -order = iv_item_order. ELSE. -path = is_prefix-path && is_prefix-name && -path. ENDIF. diff --git a/src/json/zcl_abapgit_ajson.clas.testclasses.abap b/src/json/zcl_abapgit_ajson.clas.testclasses.abap index 344c440c8..be3bf55a6 100644 --- a/src/json/zcl_abapgit_ajson.clas.testclasses.abap +++ b/src/json/zcl_abapgit_ajson.clas.testclasses.abap @@ -75,6 +75,7 @@ CLASS ltcl_parser_test DEFINITION FINAL METHODS setup. METHODS parse FOR TESTING RAISING zcx_abapgit_ajson_error. + METHODS parse_keeping_order FOR TESTING RAISING zcx_abapgit_ajson_error. METHODS parse_string FOR TESTING RAISING zcx_abapgit_ajson_error. METHODS parse_number FOR TESTING RAISING zcx_abapgit_ajson_error. METHODS parse_float FOR TESTING RAISING zcx_abapgit_ajson_error. @@ -347,6 +348,67 @@ CLASS ltcl_parser_test IMPLEMENTATION. ENDMETHOD. + METHOD parse_keeping_order. + + DATA lo_cut TYPE REF TO lcl_json_parser. + DATA lt_act TYPE zif_abapgit_ajson_types=>ty_nodes_tt. + DATA lo_nodes TYPE REF TO lcl_nodes_helper. + + CREATE OBJECT lo_nodes. + lo_nodes->add( ' | |object | | |8 |0' ). + lo_nodes->add( '/ |string |str |abc | |0 |1' ). + lo_nodes->add( '/ |number |num |123 | |0 |2' ). + lo_nodes->add( '/ |float |num |123.45 | |0 |3' ). + lo_nodes->add( '/ |boolean |bool |true | |0 |4' ). + lo_nodes->add( '/ |false |bool |false | |0 |5' ). + lo_nodes->add( '/ |null |null | | |0 |6' ). + lo_nodes->add( '/ |date |str |2020-03-15 | |0 |7' ). + lo_nodes->add( '/ |issues |array | | |2 |8' ). + lo_nodes->add( '/issues/ |1 |object | |1 |5 |0' ). + lo_nodes->add( '/issues/1/ |message |str |Indentation problem ... | |0 |1' ). + lo_nodes->add( '/issues/1/ |key |str |indentation | |0 |2' ). + lo_nodes->add( '/issues/1/ |start |object | | |2 |3' ). + lo_nodes->add( '/issues/1/start/ |row |num |4 | |0 |1' ). + lo_nodes->add( '/issues/1/start/ |col |num |3 | |0 |2' ). + lo_nodes->add( '/issues/1/ |end |object | | |2 |4' ). + lo_nodes->add( '/issues/1/end/ |row |num |4 | |0 |1' ). + lo_nodes->add( '/issues/1/end/ |col |num |26 | |0 |2' ). + lo_nodes->add( '/issues/1/ |filename |str |./zxxx.prog.abap | |0 |5' ). + lo_nodes->add( '/issues/ |2 |object | |2 |5 |0' ). + lo_nodes->add( '/issues/2/ |message |str |Remove space before XXX | |0 |1' ). + lo_nodes->add( '/issues/2/ |key |str |space_before_dot | |0 |2' ). + lo_nodes->add( '/issues/2/ |start |object | | |2 |3' ). + lo_nodes->add( '/issues/2/start/ |row |num |3 | |0 |1' ). + lo_nodes->add( '/issues/2/start/ |col |num |21 | |0 |2' ). + lo_nodes->add( '/issues/2/ |end |object | | |2 |4' ). + lo_nodes->add( '/issues/2/end/ |row |num |3 | |0 |1' ). + lo_nodes->add( '/issues/2/end/ |col |num |22 | |0 |2' ). + lo_nodes->add( '/issues/2/ |filename |str |./zxxx.prog.abap | |0 |5' ). + + CREATE OBJECT lo_cut. + lt_act = lo_cut->parse( + iv_json = sample_json( ) + iv_keep_item_order = abap_true ). + cl_abap_unit_assert=>assert_equals( + act = lt_act + exp = lo_nodes->mt_nodes ). + + lt_act = lo_cut->parse( + iv_json = sample_json( |{ cl_abap_char_utilities=>newline }| ) + iv_keep_item_order = abap_true ). + cl_abap_unit_assert=>assert_equals( + act = lt_act + exp = lo_nodes->mt_nodes ). + + lt_act = lo_cut->parse( + iv_json = sample_json( |{ cl_abap_char_utilities=>cr_lf }| ) + iv_keep_item_order = abap_true ). + cl_abap_unit_assert=>assert_equals( + act = lt_act + exp = lo_nodes->mt_nodes ). + + ENDMETHOD. + METHOD duplicate_key. DATA lo_cut TYPE REF TO lcl_json_parser. @@ -2101,6 +2163,7 @@ CLASS ltcl_writer_test DEFINITION FINAL METHODS setx FOR TESTING RAISING zcx_abapgit_ajson_error. METHODS setx_float FOR TESTING RAISING zcx_abapgit_ajson_error. METHODS setx_complex FOR TESTING RAISING zcx_abapgit_ajson_error. + METHODS setx_complex_w_keep_order FOR TESTING RAISING zcx_abapgit_ajson_error. METHODS set_with_type_slice IMPORTING @@ -3324,6 +3387,43 @@ CLASS ltcl_writer_test IMPLEMENTATION. ENDMETHOD. + METHOD setx_complex_w_keep_order. + + DATA li_cut TYPE REF TO zif_abapgit_ajson. + DATA: + BEGIN OF ls_dummy, + f TYPE i VALUE 5, + e TYPE i VALUE 6, + END OF ls_dummy. + + li_cut = zcl_abapgit_ajson=>new( iv_keep_item_order = abap_true ). + li_cut->setx( '/c:3' ). + li_cut->set( + iv_path = '/b' + iv_val = ls_dummy ). + li_cut->setx( '/a:1' ). + + cl_abap_unit_assert=>assert_equals( + act = li_cut->stringify( ) + exp = '{"c":3,"b":{"f":5,"e":6},"a":1}' ). + + li_cut->setx( '/b:{"z":9,"y":8}' ). + + cl_abap_unit_assert=>assert_equals( + act = li_cut->stringify( ) + exp = '{"c":3,"b":{"z":9,"y":8},"a":1}' ). + " TODO: a subtle bug here. The '/b:{"z":9,"y":8}' creates a json internally + " without the ordering. It's just by chance that this UT passes, but the implementation + " does not guarantee it. The parser should be instructed to keep the order of the parsed json + + li_cut->setx( '/0:9' ). + + cl_abap_unit_assert=>assert_equals( + act = li_cut->stringify( ) + exp = '{"c":3,"b":{"z":9,"y":8},"a":1,"0":9}' ). + + ENDMETHOD. + ENDCLASS.