Author: Alexey Arsenyev
Submitted: 12.05.2017
Other code samples from me:
Why
Sometimes you need to access ABAP data objects dynamically. For example, when:
- you do not know the structure of the ABAP object and to dynamically access value of the field forced to use ASSIGN .. COMPONENT with field symbol
- you do a cross-release development in ABAP and do not know if some field exists, so need dynamic access to data
- you access data from optional ABAP component and also need dynamic access to ABAP structure field or internal table row
- you need a key or index access to a dynamic internal table
Normally, if you need to access ABAP fields/structure component dynamically, you will end up in coding like this:
FIELD-SYMBOLS: <table> TYPE ANY TABLE,
<line> TYPE ANY,
<field> TYPE ANY.
ASSIGN COMPONENT `FIELD1` OF STRUCTURE <line> TO <field>.
IF <field> IS ASSIGNED.
WRITE: <field>.
ENDIF.
That may be OK (however still not very convenient), but if you have a dynamic ABAP object with deeper structure, it becomes boring:
FIELD-SYMBOLS:
<table> TYPE ANY TABLE,
<line> TYPE ANY,
<field1> TYPE ANY,
<field2> TYPE ANY,
<field3> TYPE ANY,
<field4> TYPE ANY.
ASSIGN COMPONENT `FIELD1` OF STRUCTURE <line> TO <field1>.
IF <field> IS ASSIGNED.
ASSIGN COMPONENT `CHILD_1` OF STRUCTURE <fielld1> TO <field2>.
IF <field2> IS ASSIGNED.
ASSIGN COMPONENT `CHILD_11` OF STRUCTURE <fielld2> TO <field3>.
IF <field3> IS ASSIGNED.
ASSIGN COMPONENT `CHILD_111` OF STRUCTURE <fielld3> TO <field4>.
IF <field4> IS ASSIGNED.
WRITE: <field4>.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
And if one thinks about accessing dynamically elements from nested tables it would be at all - hell.
So, below one can find a helper class, which may help in such cases, in a lean and tasty way. 
An original and actual version of the source can be found in class /UI2/CL_DATA_ACCESS delivered with UI2 Add-on (can be applied to SAP_BASIS 700 – 76X). So, you can use this ABAP JSON parser in your standard code mostly on any system. Delivered with note 2526405.
What it can
The accessor is a single class, with the following features:
- the traversing of any data object (classes are not yet supported) passed as a reference to data or as a data.
- traversing nested objects without any level limitations
- automatically resolving reference variables (REF TO fields accessed the same way as standard fields)
- a method like or XPath-like ways of accessing data
- read/modification access to elementary types
- accessing table rows using index or key
Limitations
The code of dynamic data accessor class does not pretend to be a complete and fully robust solution, but it may become like this if requests will come 
Current limitations are as following:
- only ABAP data structures are supported. Traversing of ABAP objects/classes not yet supported, however possible
- dynamic modification of tables (not data inside) is not supported. E.g you can not add/remove rows with API, but you can do it via reference to the table
- the syntax for key access of the rows in dynamic tables does not allow usage of the symbol "," as part of the query. No escaping is supported.
Usage
Necessary declarations:
TYPES:
BEGIN OF t_properties,
enabled TYPE abap_bool,
length TYPE i,
description TYPE c LENGTH 20,
END OF t_properties,
BEGIN OF t_data,
id TYPE i,
properties TYPE t_properties,
content TYPE string,
END OF t_data,
BEGIN OF t_name_value,
name TYPE string,
value TYPE string,
END OF t_name_value,
BEGIN OF t_example,
flag TYPE abap_bool,
props TYPE t_data,
params TYPE SORTED TABLE OF t_name_value WITH UNIQUE KEY name,
END OF t_example.
DATA: ls_data TYPE t_example,
lr_data TYPE REF TO data,
lr_ref TYPE REF TO data,
lv_int TYPE i,
lo_data TYPE REF TO /ui2/cl_data_access.
FIELD-SYMBOLS: <data> TYPE data.
ls_data-props-id = 12345.
ls_data-props-content = `Some Content`.
ls_data-props-properties-enabled = abap_false.
ls_data-props-properties-length = 10.
ls_data-props-properties-description = `My description`.
GET REFERENCE OF ls_data INTO lr_data.
Simplest usage example:
CREATE OBJECT lo_data EXPORTING ir_data = lr_data.
" standard way (does not work on SAP_BASIS 700)
lr_ref = lo_data->at(`PROPS`)->at(`id`)->ref( ).
IF lr_ref IS BOUND.
ASSIGN lr_ref->* TO <data>.
WRITE: <data>.
ENDIF.
" XPath like
lr_ref = lo_data->at(`PROPS-ID`)->ref( ).
IF lr_ref IS BOUND.
ASSIGN lr_ref->* TO <data>.
WRITE: <data>.
ENDIF.
" using helper method for creation
lr_ref = /ui2/cl_data_access=>create( ir_data = lr_data iv_component = `PROPS-ID`)->ref( ).
IF lr_ref IS BOUND.
ASSIGN lr_ref->* TO <data>.
WRITE: <data>.
ENDIF.
" reading value directly
lo_data->at(`props-properties-length`)->value( IMPORTING ev_data = lv_int ).
WRITE: lv_int.
If you want to use modification operations, you can only operate with references when creating an accessor object.
" modifing value
lr_ref = lo_data->at(`props-properties-length`)->ref( ).
ASSIGN lr_ref->* TO <data>.
<data> = 25.
" or even more simple
lo_data->at(`props-properties-length`)->set( 15 ).
The code is robust - accessing of a not existing component of any level will not result in a crash but will return an empty reference or initial value.
" reading not existing value returns initial value
lo_data->at(`props-properties-not_exist-length-not-exist`)->value( IMPORTING ev_data = lv_int ).
WRITE: lv_int. " -> 0
Working with tables:
DATA:
ls_data TYPE t_example,
ls_line LIKE LINE OF ls_data-params,
lv_value TYPE string,
lo_data TYPE REF TO /ui2/cl_data_access.
ls_line-name = `KEY1`.
ls_line-value = `Value1`.
INSERT ls_line INTO TABLE ls_data-params.
ls_line-name = `KEY2`.
ls_line-value = `Value2`.
INSERT ls_line INTO TABLE ls_data-params.
/ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[2]-name`)->value( IMPORTING ev_data = lv_value ).
WRITE: lv_value.
/ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[name=KEY1]-value`)->value( IMPORTING ev_data = lv_value ).
WRITE: lv_value.
/ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params`)->at(`[name=KEY1]-value`)->value( IMPORTING ev_data = lv_value ).
WRITE: lv_value.
/ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[name=KEY1, value=Value1]-value`)->value( IMPORTING ev_data = lv_value ).
WRITE: lv_value.
API
CREATE - Static Method Public Helper method for creating an instance of dynamic accessor
- > IR_DATA (ref to data)Importing Type Ref To DATA Reference to data (allows modification of embedded data)
- > IV_DATA (data) - any data (modification of embedded data not allowed)
- > IV_COMPONENT (string) - Sub-component name (XPath-like syntax is supported)
- < RO_REF (ref to /ui2/cl_data_access) - Reference to accessor object pointing to subcomponent
CONSTRUCTOR - Instance Method Public Constructor
- > IR_DATA (ref to data)Importing Type Ref To DATA Reference to data (allows modification of embedded data)
- > IV_DATA (data) - any data (modification of embedded data not allowed)
AT - Instance Method Public Component accessor
- > IV_COMPONENT (string) - Sub-component name (XPath-like syntax is supported)
- < RO_REF (ref to /ui2/cl_data_access) - Reference to accessor object pointing to subcomponent
EMPTY - Instance Method Public Returns TRUE if the embedded object is initial (not bound)
- < RV_VAL (boolean) - ABAP_TRUE if the object is initial and data is not bound
REF - Instance Method Public Returns a reference to the embedded object
- < RV_DATA (ref to data) - Reference to embedded data
VALUE - Instance Method Public Returns copy of the value
- < EV_DATA (data) - Copy of the embedded data value, or initial if data is not bound
SET - Instance Method Public Sets the value of the embedded object, if not initial
- > IV_DATA (data) - New value for embedded object
- < RV_SUCCESS (boolean) - ABAP_TRUE, if data was successfully modified
XPath-like dynamic data access
To access nested components you can use a nice, object-oriented way, using nested calls of AT method (it is robust, and would not crash accessing not existing components), as
lo_object->at('subcomp1')->at('subcomp11')->...
But if you prefer more compact form, or run code on SAP_BASIS < 702 (nested method calls are not supported), you may use XPath-like syntax for accessing components. Like this:
lo_object->at('subcomp1-subcomp11-subcomp111')
or like this
lo_object->at('subcomp1->subcomp11->subcomp111')
The syntax:
- You can use any symbol (or combinations of symbols) as a component separator, except "[", "]", "=", ",". Recommended separator symbol is "-".
- For dynamic index access of rows in nested tables use "[index]" after the component name. E.g. "table_name[2]". The indexing starts at 1. If the accessor object references table data object directly, you may skip the component name. E.g. "[2]". You may continue accessing components after index access, e.g. : "table_a[1]-struct-table_b[2]". If you do out of range access, you got an empty reference back. Index access works only with index tables (STANDARD, SORTED).
- For dynamic key access of rows in nested tables use "(key=value)" for single key lookup, "(key1=value1, key2=value2)" for multi-key lookup, "(value)" for table line lookup. You can NOT search for values containing ",". You can use nested lookups: "table_a(key1=value1)-table_b(key2=value2, key3=value3)". Values used in a query shall be assignable to field structures.
The Code
The actual code for the helper:
*----------------------------------------------------------------------*
* CLASS /ui2/cl_data_access DEFINITION
*----------------------------------------------------------------------*
* Documentation can be found on SCN:
" https://wiki.scn.sap.com/wiki/display/Snippets/Dynamic+Data+Accessor+Helper+Class+for+ABAP
*----------------------------------------------------------------------*
CLASS /ui2/cl_data_access DEFINITION.
PUBLIC SECTION.
CLASS-METHODS create
IMPORTING
ir_data TYPE REF TO data OPTIONAL
iv_data TYPE data OPTIONAL
iv_component TYPE string OPTIONAL
RETURNING
VALUE(ro_ref) TYPE REF TO /ui2/cl_data_access .
METHODS constructor
IMPORTING
ir_data TYPE REF TO data OPTIONAL
iv_data TYPE data OPTIONAL .
METHODS at
IMPORTING
iv_component TYPE string OPTIONAL
RETURNING
VALUE(ro_ref) TYPE REF TO /ui2/cl_data_access .
METHODS empty
RETURNING
VALUE(rv_val) TYPE abap_bool .
METHODS ref
RETURNING
VALUE(rv_data) TYPE REF TO data .
METHODS value
EXPORTING
ev_data TYPE data .
METHODS set
IMPORTING
iv_data TYPE data
RETURNING
VALUE(rv_success) TYPE abap_bool .
PROTECTED SECTION.
DATA mr_data TYPE REF TO data .
CLASS-METHODS deref
IMPORTING
ir_data TYPE REF TO data
RETURNING
VALUE(rr_data) TYPE REF TO data .
METHODS at_int
IMPORTING
iv_component TYPE string OPTIONAL
iv_index TYPE i OPTIONAL
iv_keys TYPE string OPTIONAL
RETURNING
VALUE(ro_ref) TYPE REF TO /ui2/cl_data_access .
ENDCLASS.
CLASS /ui2/cl_data_access IMPLEMENTATION.
METHOD at.
DATA:
lv_component TYPE string,
lv_sindex TYPE string,
lv_keys TYPE string,
lv_index TYPE i,
lt_hier TYPE match_result_tab.
FIELD-SYMBOLS:
<component> LIKE LINE OF lt_hier,
<sub_match> TYPE LINE OF submatch_result_tab.
FIND ALL OCCURRENCES OF REGEX `(\w+)?(?:\[(?:(\d+)|([^\]]+))\])?` IN iv_component RESULTS lt_hier.
ro_ref = me.
LOOP AT lt_hier ASSIGNING <component> WHERE length IS NOT INITIAL.
CHECK ro_ref->empty( ) EQ abap_false.
READ TABLE <component>-submatches INDEX 1 ASSIGNING <sub_match>.
IF <sub_match>-length IS INITIAL.
CLEAR lv_component.
ELSE.
lv_component = iv_component+<sub_match>-offset(<sub_match>-length).
TRANSLATE lv_component TO UPPER CASE.
ENDIF.
READ TABLE <component>-submatches INDEX 2 ASSIGNING <sub_match>.
IF <sub_match>-length IS INITIAL.
CLEAR lv_index.
ELSE.
lv_index = lv_sindex = iv_component+<sub_match>-offset(<sub_match>-length).
ENDIF.
READ TABLE <component>-submatches INDEX 3 ASSIGNING <sub_match>.
IF <sub_match>-length IS INITIAL.
CLEAR lv_keys.
ELSE.
lv_keys = iv_component+<sub_match>-offset(<sub_match>-length).
ENDIF.
ro_ref = ro_ref->at_int( iv_component = lv_component iv_index = lv_index iv_keys = lv_keys ).
ENDLOOP.
ENDMETHOD. "at_int
METHOD at_int.
DATA: lv_key TYPE string,
lv_value TYPE string,
lt_keys TYPE match_result_tab,
lr_data TYPE REF TO data,
lo_type TYPE REF TO cl_abap_typedescr.
FIELD-SYMBOLS: <data> TYPE data,
<comp> TYPE data,
<key> LIKE LINE OF lt_keys,
<sub_match> TYPE LINE OF submatch_result_tab,
<ref> TYPE REF TO data,
<table> TYPE ANY TABLE,
<idx_table> TYPE INDEX TABLE.
IF mr_data IS BOUND.
IF iv_component IS NOT INITIAL.
ASSIGN mr_data->* TO <data>.
ASSIGN COMPONENT iv_component OF STRUCTURE <data> TO <comp>.
IF <comp> IS ASSIGNED.
GET REFERENCE OF <comp> INTO lr_data.
lr_data = deref( lr_data ).
ASSIGN lr_data TO <ref>.
ENDIF.
ELSE.
ASSIGN mr_data TO <ref>.
ENDIF.
ENDIF.
IF <ref> IS ASSIGNED AND ( iv_index IS NOT INITIAL OR iv_keys IS NOT INITIAL ).
lo_type = cl_abap_typedescr=>describe_by_data_ref( <ref> ).
IF lo_type->kind EQ cl_abap_typedescr=>kind_table.
" check for table index access
IF iv_index IS NOT INITIAL.
ASSIGN <ref>->* TO <idx_table>.
IF sy-subrc IS INITIAL.
READ TABLE <idx_table> INDEX iv_index REFERENCE INTO <ref>.
IF sy-subrc IS NOT INITIAL.
UNASSIGN <ref>.
ENDIF.
ELSE.
UNASSIGN <ref>.
ENDIF.
ELSEIF iv_keys IS NOT INITIAL.
ASSIGN <ref>->* TO <table>.
IF sy-subrc IS INITIAL.
CREATE DATA lr_data LIKE LINE OF <table>.
ASSIGN lr_data->* TO <data>.
FIND ALL OCCURRENCES OF REGEX `(\w+)\s*=\s*([^,]*),?` IN iv_keys RESULTS lt_keys.
IF sy-subrc IS INITIAL.
LOOP AT lt_keys ASSIGNING <key>.
READ TABLE <key>-submatches INDEX 1 ASSIGNING <sub_match>.
lv_key = iv_keys+<sub_match>-offset(<sub_match>-length).
TRANSLATE lv_key TO UPPER CASE.
READ TABLE <key>-submatches INDEX 2 ASSIGNING <sub_match>.
lv_value = iv_keys+<sub_match>-offset(<sub_match>-length).
ASSIGN COMPONENT lv_key OF STRUCTURE <data> TO <comp>.
CHECK sy-subrc IS INITIAL.
<comp> = lv_value.
ENDLOOP.
ELSE.
<data> = lv_key.
ENDIF.
READ TABLE <table> FROM <data> REFERENCE INTO <ref>.
IF sy-subrc IS NOT INITIAL.
UNASSIGN <ref>.
ENDIF.
ELSE.
UNASSIGN <ref>.
ENDIF.
ENDIF.
ENDIF.
ENDIF.
IF <ref> IS ASSIGNED.
CREATE OBJECT ro_ref
EXPORTING
ir_data = <ref>.
ELSE.
CREATE OBJECT ro_ref.
ENDIF.
ENDMETHOD. "at_int
METHOD constructor.
IF ir_data IS NOT INITIAL.
mr_data = ir_data.
ELSEIF iv_data IS SUPPLIED.
GET REFERENCE OF iv_data INTO mr_data.
ENDIF.
mr_data = deref( mr_data ).
ENDMETHOD. "constructor
METHOD create.
IF iv_data IS SUPPLIED.
CREATE OBJECT ro_ref
EXPORTING
iv_data = iv_data.
ELSE.
CREATE OBJECT ro_ref
EXPORTING
ir_data = ir_data.
ENDIF.
IF iv_component IS NOT INITIAL.
ro_ref = ro_ref->at( iv_component ).
ENDIF.
ENDMETHOD. "create
METHOD deref.
DATA: lo_type TYPE REF TO cl_abap_typedescr.
FIELD-SYMBOLS: <data> TYPE data.
rr_data = ir_data.
IF rr_data IS NOT INITIAL.
lo_type = cl_abap_typedescr=>describe_by_data_ref( ir_data ).
IF lo_type->kind EQ cl_abap_typedescr=>kind_ref.
ASSIGN ir_data->* TO <data>.
rr_data = deref( <data> ).
ENDIF.
ENDIF.
ENDMETHOD. "deref
METHOD empty.
IF mr_data IS INITIAL.
rv_val = abap_true.
ENDIF.
ENDMETHOD. "empty
METHOD ref.
rv_data = mr_data.
ENDMETHOD. "ref
METHOD set.
FIELD-SYMBOLS: <data> TYPE data.
IF mr_data IS BOUND.
ASSIGN mr_data->* TO <data>.
<data> = iv_data.
rv_success = abap_true.
ENDIF.
ENDMETHOD.
METHOD value.
DATA: lo_type_out TYPE REF TO cl_abap_typedescr,
lo_type_in TYPE REF TO cl_abap_typedescr.
FIELD-SYMBOLS: <data> TYPE data.
CLEAR ev_data.
IF mr_data IS BOUND.
ASSIGN mr_data->* TO <data>.
lo_type_out = cl_abap_typedescr=>describe_by_data( ev_data ).
lo_type_in = cl_abap_typedescr=>describe_by_data( <data> ).
IF lo_type_out->kind EQ lo_type_in->kind.
ev_data = <data>.
ENDIF.
ENDIF.
ENDMETHOD. "value
ENDCLASS.
And unit tests, if you would like to do your modifications and want to validate that it still works:
CLASS lc_ut DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PUBLIC SECTION.
TYPES:
BEGIN OF t_properties,
enabled TYPE abap_bool,
length TYPE i,
description TYPE c LENGTH 20,
END OF t_properties,
BEGIN OF t_data,
id TYPE i,
properties TYPE t_properties,
content TYPE string,
END OF t_data,
BEGIN OF t_name_value,
name TYPE string,
value TYPE string,
END OF t_name_value,
BEGIN OF t_example,
flag TYPE abap_bool,
props TYPE t_data,
params TYPE SORTED TABLE OF t_name_value WITH UNIQUE KEY name,
END OF t_example.
METHODS: test_basic_actions FOR TESTING.
METHODS: test_table_access FOR TESTING.
ENDCLASS. "lc_ut
CLASS lc_ut IMPLEMENTATION.
METHOD test_basic_actions.
DATA: ls_data TYPE t_example,
lr_data TYPE REF TO data,
lr_ref TYPE REF TO data,
lv_int TYPE i,
lo_data TYPE REF TO /ui2/cl_data_access.
FIELD-SYMBOLS: <data> TYPE data.
ls_data-props-id = 12345.
ls_data-props-content = `Some Content`.
ls_data-props-properties-enabled = abap_false.
ls_data-props-properties-length = 10.
ls_data-props-properties-description = `My description`.
GET REFERENCE OF ls_data INTO lr_data.
CREATE OBJECT lo_data EXPORTING ir_data = lr_data.
" standard way (does not work on SAP_BASIS 700)
lr_ref = lo_data->at(`PROPS`)->at(`id`)->ref( ).
cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property fails!` ).
ASSIGN lr_ref->* TO <data>.
cl_aunit_assert=>assert_equals( act = <data> exp = ls_data-props-id msg = `Dynamic access to existing property fails!` ).
lr_ref = lo_data->at(`PROPS`)->at(`key`)->ref( ).
cl_aunit_assert=>assert_not_bound( act = lr_ref msg = `Dynamic access to NOT existing property fails!` ).
" XPath like
lr_ref = lo_data->at(`PROPS-ID`)->ref( ).
cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property fails!` ).
ASSIGN lr_ref->* TO <data>.
cl_aunit_assert=>assert_equals( act = <data> exp = ls_data-props-id msg = `Dynamic access to existing property fails!` ).
" using helper method for creation
lr_ref = /ui2/cl_data_access=>create( ir_data = lr_data iv_component = `PROPS-ID`)->ref( ).
cl_aunit_assert=>assert_bound( act = lr_ref msg = `Dynamic access to existing property fails!` ).
ASSIGN lr_ref->* TO <data>.
cl_aunit_assert=>assert_equals( act = <data> exp = ls_data-props-id msg = `Dynamic access to existing property fails!` ).
" reading value
lo_data->at(`props-properties-length`)->value( IMPORTING ev_data = lv_int ).
cl_aunit_assert=>assert_equals( act = lv_int exp = ls_data-props-properties-length msg = `Dynamic read of existing the property fails!` ).
lv_int = 25.
lo_data->at(`props-properties-len`)->value( IMPORTING ev_data = lv_int ).
cl_aunit_assert=>assert_initial( act = lv_int msg = `Dynamic read of the NOT existing property fails!` ).
" modifing value
lv_int = 25.
lr_ref = lo_data->at(`props-properties-length`)->ref( ).
ASSIGN lr_ref->* TO <data>.
<data> = lv_int.
cl_aunit_assert=>assert_equals( act = lv_int exp = ls_data-props-properties-length msg = `Dynamic modification of the existing property fails!` ).
lv_int = 15.
lo_data->at(`props-properties-length`)->set( lv_int ).
cl_aunit_assert=>assert_equals( act = lv_int exp = ls_data-props-properties-length msg = `Dynamic modification of the existing property fails!` ).
" degenerated example
lv_int = 15.
lo_data = /ui2/cl_data_access=>create( iv_data = lv_int ).
ASSIGN lr_ref->* TO <data>.
cl_aunit_assert=>assert_equals( act = <data> exp = lv_int msg = `Dynamic access of existing the property fails!` ).
ENDMETHOD.
METHOD test_table_access.
DATA:
ls_data TYPE t_example,
ls_line LIKE LINE OF ls_data-params,
lv_value TYPE string,
lo_data TYPE REF TO /ui2/cl_data_access.
ls_line-name = `KEY1`.
ls_line-value = `Value1`.
INSERT ls_line INTO TABLE ls_data-params.
ls_line-name = `KEY2`.
ls_line-value = `Value2`.
INSERT ls_line INTO TABLE ls_data-params.
lo_data = /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[2]-name`).
lo_data->value( IMPORTING ev_data = lv_value ).
cl_aunit_assert=>assert_equals( act = lv_value exp = `KEY2` msg = `Dynamic read to table item by index fails!` ).
lo_data = /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[name=KEY1]-value`).
lo_data->value( IMPORTING ev_data = lv_value ).
cl_aunit_assert=>assert_equals( act = lv_value exp = `Value1` msg = `Dynamic read to table item by key index fails!` ).
lo_data = /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params`)->at(`[name=KEY1]-value`).
lo_data->value( IMPORTING ev_data = lv_value ).
cl_aunit_assert=>assert_equals( act = lv_value exp = `Value1` msg = `Dynamic read to table item by key index fails!` ).
lo_data = /ui2/cl_data_access=>create( iv_data = ls_data iv_component = `params[name=KEY1, value=Value1]-value`).
lo_data->value( IMPORTING ev_data = lv_value ).
cl_aunit_assert=>assert_equals( act = lv_value exp = `Value1` msg = `Dynamic read to table item by key index fails!` ).
ENDMETHOD.
ENDCLASS.
Version History
- Fixed. Access of fields with special characters in name (e.g "/BIC/YEAR") fails.
- Fixed. Short dump, when accessing elements of null array
- New: /UI2/CL_DATA_ACCESS class for working with dynamic ABAP data object (generated with method /UI2/CL_JSON=>GENERATE). The class can be used as a replacement for multiple ASSIGN COMPONENT language constructions.
3 Comments
Bilen Cekic
Great class but just want to report a bug;
if you are using BW objects (which mainly contains /BIC/ZOBJ/ for field names in custom info object table), it is not working (maybe my parameters are wrong ? ).
Sample;
ZREFHELPER_CL=>CREATE( IR_DATA = LR_DATA IV_COMPONENT = `[1]`)->AT(`/BIC/Z_YEAR`)->VALUE( IMPORTING EV_DATA = LV_YEAR ).
here lr_data is a data reference with standard internal table and contains a column named /BIC/Z_YEAR. Above code won't work due to regular expression is causing issue for char `/`. I changed code accordingly to fit my scenario but wanted to ask if there is a way to do it ? (and finally can comment)
what i did is; i sent my parameter `BICZ_YEAR` without `/` and inside the AT_INT method; i added below dirty code; (it is working hehe )
replace first OCCURRENCE OF 'BIC' in lv_Component with '/BIC/'.
Alexey Arsenyev
Hello Bilen,
thanks for feedback and tests!
Yes, it is a bug. The code was assuming that ABAP field names can contain only alphabetical characters and numbers. But of course - it is ABAP
I have added support for "/" in names now. Hope we do not need some other special characters here
The right regexp would be:
The fix would be available in next patch for /UI2/CL_JSON (I am bundling this in same note).
If you have more feedback or ideas it would be highly appreciated
BR, Alexey.
Bilen Cekic
awesome Alexey ! thanks for the quick fix. i changed my code accordingly.