Dynamic Programming enables the creation and manipulation of data objects at runtime. With RTTS (Runtime Type Services) and dynamic statements, flexible, generic programs can be developed.
RTTS Classes
| Class | Description |
|---|---|
CL_ABAP_TYPEDESCR | Base class for type descriptions |
CL_ABAP_ELEMDESCR | Elementary types (i, string, etc.) |
CL_ABAP_STRUCTDESCR | Structures |
CL_ABAP_TABLEDESCR | Internal tables |
CL_ABAP_REFDESCR | References |
CL_ABAP_CLASSDESCR | Classes |
CL_ABAP_OBJECTDESCR | Objects |
Examples
1. Determine Type of Data Object
DATA: lv_string TYPE string VALUE 'Test', lt_table TYPE TABLE OF mara, lo_struct TYPE REF TO kna1.
" Determine typeDATA(lo_type1) = cl_abap_typedescr=>describe_by_data( lv_string ).DATA(lo_type2) = cl_abap_typedescr=>describe_by_data( lt_table ).DATA(lo_type3) = cl_abap_typedescr=>describe_by_name( 'KNA1' ).
WRITE: / 'String type:', lo_type1->type_kind, / 'Table type:', lo_type2->type_kind, / 'KNA1 type:', lo_type3->type_kind.
" type_kind values:" C = Character, N = Numeric, I = Integer, P = Packed" D = Date, T = Time, X = Hex, F = Float" g = String, h = Table, u = Structure, v = Class2. Read Structure Components
DATA: ls_customer TYPE kna1.
" Describe structureDATA(lo_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( ls_customer )).
" Output all componentsLOOP AT lo_struct->components INTO DATA(ls_comp). WRITE: / ls_comp-name, ls_comp-type_kind, ls_comp-length.ENDLOOP.
" Search for specific componentREAD TABLE lo_struct->components INTO ls_comp WITH KEY name = 'KUNNR'.
IF sy-subrc = 0. WRITE: / 'KUNNR found, length:', ls_comp-length.ENDIF.3. Create Dynamic Structure
" Define componentsDATA(lt_components) = VALUE cl_abap_structdescr=>component_table( ( name = 'FIELD1' type = cl_abap_elemdescr=>get_c( 10 ) ) ( name = 'FIELD2' type = cl_abap_elemdescr=>get_i( ) ) ( name = 'FIELD3' type = cl_abap_elemdescr=>get_string( ) ) ( name = 'FIELD4' type = cl_abap_elemdescr=>get_d( ) )).
" Create structure typeDATA(lo_struct_type) = cl_abap_structdescr=>create( lt_components ).
" Create data objectDATA: lr_struct TYPE REF TO data.CREATE DATA lr_struct TYPE HANDLE lo_struct_type.
" Fill with dataASSIGN lr_struct->* TO FIELD-SYMBOL(<fs_struct>).ASSIGN COMPONENT 'FIELD1' OF STRUCTURE <fs_struct> TO FIELD-SYMBOL(<fv_field1>).<fv_field1> = 'Hello'.
ASSIGN COMPONENT 'FIELD2' OF STRUCTURE <fs_struct> TO FIELD-SYMBOL(<fv_field2>).<fv_field2> = 42.4. Create Dynamic Internal Table
" Structure type (as above)DATA(lt_components) = VALUE cl_abap_structdescr=>component_table( ( name = 'KUNNR' type = cl_abap_elemdescr=>get_c( 10 ) ) ( name = 'NAME1' type = cl_abap_elemdescr=>get_c( 35 ) ) ( name = 'AMOUNT' type = cl_abap_elemdescr=>get_p( p_length = 8 p_decimals = 2 ) )).
DATA(lo_struct_type) = cl_abap_structdescr=>create( lt_components ).
" Create table typeDATA(lo_table_type) = cl_abap_tabledescr=>create( p_line_type = lo_struct_type p_table_kind = cl_abap_tabledescr=>tablekind_std).
" Create tableDATA: lr_table TYPE REF TO data.CREATE DATA lr_table TYPE HANDLE lo_table_type.
ASSIGN lr_table->* TO FIELD-SYMBOL(<ft_table>).
" Add rowDATA: lr_line TYPE REF TO data.CREATE DATA lr_line TYPE HANDLE lo_struct_type.ASSIGN lr_line->* TO FIELD-SYMBOL(<fs_line>).
ASSIGN COMPONENT 'KUNNR' OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fv>).<fv> = '0000001000'.ASSIGN COMPONENT 'NAME1' OF STRUCTURE <fs_line> TO <fv>.<fv> = 'Test Customer'.ASSIGN COMPONENT 'AMOUNT' OF STRUCTURE <fs_line> TO <fv>.<fv> = '1234.56'.
INSERT <fs_line> INTO TABLE <ft_table>.5. Dynamic SELECT
DATA: lv_tablename TYPE tabname VALUE 'KNA1', lv_fields TYPE string VALUE 'KUNNR NAME1 ORT01', lv_where TYPE string VALUE 'LAND1 = ''DE''', lr_data TYPE REF TO data.
" Determine table structureDATA(lo_table_descr) = CAST cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_name( lv_tablename )).DATA(lo_line_type) = lo_table_descr->get_table_line_type( ).
" Create dynamic tableDATA(lo_dyn_table) = cl_abap_tabledescr=>create( lo_line_type ).CREATE DATA lr_data TYPE HANDLE lo_dyn_table.ASSIGN lr_data->* TO FIELD-SYMBOL(<ft_result>).
" Dynamic SELECTSELECT (lv_fields) FROM (lv_tablename) WHERE (lv_where) INTO CORRESPONDING FIELDS OF TABLE @<ft_result> UP TO 100 ROWS.
" Process resultLOOP AT <ft_result> ASSIGNING FIELD-SYMBOL(<fs_row>). ASSIGN COMPONENT 'KUNNR' OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fv_kunnr>). ASSIGN COMPONENT 'NAME1' OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fv_name>). WRITE: / <fv_kunnr>, <fv_name>.ENDLOOP.6. Generic Field List
CLASS zcl_dynamic_fields DEFINITION. PUBLIC SECTION. METHODS: read_table_dynamic IMPORTING iv_table TYPE tabname it_fields TYPE string_table iv_where TYPE string OPTIONAL EXPORTING et_data TYPE REF TO data.ENDCLASS.
CLASS zcl_dynamic_fields IMPLEMENTATION. METHOD read_table_dynamic. " Field list as string DATA(lv_fields) = concat_lines_of( table = it_fields sep = ` ` ).
" Create structure with only desired fields DATA(lo_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( iv_table ) ).
DATA(lt_components) = VALUE cl_abap_structdescr=>component_table( ). LOOP AT it_fields INTO DATA(lv_field). READ TABLE lo_struct->components INTO DATA(ls_comp) WITH KEY name = to_upper( lv_field ). IF sy-subrc = 0. APPEND VALUE #( name = ls_comp-name type = lo_struct->get_component_type( ls_comp-name ) ) TO lt_components. ENDIF. ENDLOOP.
" Create dynamic table DATA(lo_new_struct) = cl_abap_structdescr=>create( lt_components ). DATA(lo_new_table) = cl_abap_tabledescr=>create( lo_new_struct ).
CREATE DATA et_data TYPE HANDLE lo_new_table. ASSIGN et_data->* TO FIELD-SYMBOL(<ft_data>).
" SELECT IF iv_where IS INITIAL. SELECT (lv_fields) FROM (iv_table) INTO CORRESPONDING FIELDS OF TABLE @<ft_data>. ELSE. SELECT (lv_fields) FROM (iv_table) WHERE (iv_where) INTO CORRESPONDING FIELDS OF TABLE @<ft_data>. ENDIF. ENDMETHOD.ENDCLASS.
" UsageDATA: lr_result TYPE REF TO data.
NEW zcl_dynamic_fields( )->read_table_dynamic( EXPORTING iv_table = 'MARA' it_fields = VALUE #( ( `MATNR` ) ( `MTART` ) ( `MATKL` ) ) iv_where = 'MTART = ''FERT''' IMPORTING et_data = lr_result).
ASSIGN lr_result->* TO FIELD-SYMBOL(<ft_materials>).7. Dynamic Object Instantiation
DATA: lv_classname TYPE seoclsname VALUE 'ZCL_MY_CLASS', lo_object TYPE REF TO object.
" Instantiate class dynamicallyCREATE OBJECT lo_object TYPE (lv_classname).
" With parametersDATA: lt_params TYPE abap_parmbind_tab.
lt_params = VALUE #( ( name = 'IV_PARAM1' kind = cl_abap_objectdescr=>exporting value = REF #( 'Value1' ) ) ( name = 'IV_PARAM2' kind = cl_abap_objectdescr=>exporting value = REF #( 123 ) )).
CREATE OBJECT lo_object TYPE (lv_classname) PARAMETER-TABLE lt_params.8. Dynamic Method Call
DATA: lo_object TYPE REF TO object, lv_method TYPE string VALUE 'PROCESS_DATA', lt_params TYPE abap_parmbind_tab, lt_results TYPE abap_parmbind_tab, lv_input TYPE string VALUE 'Test', lv_output TYPE string.
" Create objectCREATE OBJECT lo_object TYPE zcl_processor.
" Build parameterslt_params = VALUE #( ( name = 'IV_INPUT' kind = cl_abap_objectdescr=>exporting value = REF #( lv_input ) )).
lt_results = VALUE #( ( name = 'RV_OUTPUT' kind = cl_abap_objectdescr=>returning value = REF #( lv_output ) )).
" Call methodCALL METHOD lo_object->(lv_method) PARAMETER-TABLE lt_params EXCEPTION-TABLE lt_results.
WRITE: / 'Result:', lv_output.9. Analyze Class Methods and Attributes
DATA: lv_classname TYPE seoclsname VALUE 'CL_ABAP_TYPEDESCR'.
" Load class descriptionDATA(lo_class) = CAST cl_abap_classdescr( cl_abap_typedescr=>describe_by_name( lv_classname )).
" List methodsWRITE: / 'Methods:'.LOOP AT lo_class->methods INTO DATA(ls_method). WRITE: / '-', ls_method-name, ls_method-visibility.ENDLOOP.
" List attributesWRITE: / 'Attributes:'.LOOP AT lo_class->attributes INTO DATA(ls_attr). WRITE: / '-', ls_attr-name, ls_attr-type_kind.ENDLOOP.
" InterfacesWRITE: / 'Interfaces:'.LOOP AT lo_class->interfaces INTO DATA(ls_intf). WRITE: / '-', ls_intf-name.ENDLOOP.10. Generic Data Conversion
CLASS zcl_data_converter DEFINITION. PUBLIC SECTION. CLASS-METHODS: convert_itab_to_string_table IMPORTING it_data TYPE ANY TABLE RETURNING VALUE(rt_result) TYPE string_table.
CLASS-METHODS: structure_to_json IMPORTING is_data TYPE any RETURNING VALUE(rv_json) TYPE string.ENDCLASS.
CLASS zcl_data_converter IMPLEMENTATION. METHOD convert_itab_to_string_table. " Analyze structure DATA(lo_table) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_data ) ). DATA(lo_struct) = CAST cl_abap_structdescr( lo_table->get_table_line_type( ) ).
" Convert each row to string LOOP AT it_data ASSIGNING FIELD-SYMBOL(<fs_row>). DATA(lv_line) = ``.
LOOP AT lo_struct->components INTO DATA(ls_comp). ASSIGN COMPONENT ls_comp-name OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fv_value>).
IF lv_line IS NOT INITIAL. lv_line = lv_line && `;`. ENDIF. lv_line = lv_line && <fv_value>. ENDLOOP.
APPEND lv_line TO rt_result. ENDLOOP. ENDMETHOD.
METHOD structure_to_json. " Simple JSON conversion DATA(lo_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( is_data ) ).
rv_json = `{`. DATA(lv_first) = abap_true.
LOOP AT lo_struct->components INTO DATA(ls_comp). ASSIGN COMPONENT ls_comp-name OF STRUCTURE is_data TO FIELD-SYMBOL(<fv_value>).
IF lv_first = abap_false. rv_json = rv_json && `,`. ENDIF. lv_first = abap_false.
rv_json = rv_json && |"{ to_lower( ls_comp-name ) }":"|. rv_json = rv_json && <fv_value>. rv_json = rv_json && `"`. ENDLOOP.
rv_json = rv_json && `}`. ENDMETHOD.ENDCLASS.11. Build Dynamic WHERE Clause
CLASS zcl_where_builder DEFINITION. PUBLIC SECTION. METHODS: add_condition IMPORTING iv_field TYPE string iv_operator TYPE string iv_value TYPE any RETURNING VALUE(ro_self) TYPE REF TO zcl_where_builder.
METHODS: add_range IMPORTING iv_field TYPE string it_range TYPE ANY TABLE RETURNING VALUE(ro_self) TYPE REF TO zcl_where_builder.
METHODS: get_where RETURNING VALUE(rv_where) TYPE string.
PRIVATE SECTION. DATA: mt_conditions TYPE string_table.ENDCLASS.
CLASS zcl_where_builder IMPLEMENTATION. METHOD add_condition. DATA(lv_condition) = |{ iv_field } { iv_operator } '{ iv_value }'|. APPEND lv_condition TO mt_conditions. ro_self = me. ENDMETHOD.
METHOD add_range. DATA: lv_condition TYPE string.
LOOP AT it_range ASSIGNING FIELD-SYMBOL(<fs_range>). ASSIGN COMPONENT 'SIGN' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_sign>). ASSIGN COMPONENT 'OPTION' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_option>). ASSIGN COMPONENT 'LOW' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_low>). ASSIGN COMPONENT 'HIGH' OF STRUCTURE <fs_range> TO FIELD-SYMBOL(<fv_high>).
CASE <fv_option>. WHEN 'EQ'. lv_condition = |{ iv_field } = '{ <fv_low> }'|. WHEN 'BT'. lv_condition = |{ iv_field } BETWEEN '{ <fv_low> }' AND '{ <fv_high> }'|. WHEN 'CP'. lv_condition = |{ iv_field } LIKE '{ <fv_low> }'|. ENDCASE.
IF <fv_sign> = 'E'. lv_condition = |NOT ( { lv_condition } )|. ENDIF.
APPEND lv_condition TO mt_conditions. ENDLOOP.
ro_self = me. ENDMETHOD.
METHOD get_where. rv_where = concat_lines_of( table = mt_conditions sep = ` AND ` ). ENDMETHOD.ENDCLASS.
" UsageDATA(lo_where) = NEW zcl_where_builder( ).
DATA(lv_where) = lo_where->add_condition( iv_field = 'LAND1' iv_operator = '=' iv_value = 'DE')->add_condition( iv_field = 'KTOKD' iv_operator = '=' iv_value = '0001')->get_where( ).
" Result: LAND1 = 'DE' AND KTOKD = '0001'12. Generic Table Copy
CLASS zcl_table_copy DEFINITION. PUBLIC SECTION. CLASS-METHODS: copy_matching_fields IMPORTING it_source TYPE ANY TABLE CHANGING ct_target TYPE ANY TABLE.ENDCLASS.
CLASS zcl_table_copy IMPLEMENTATION. METHOD copy_matching_fields. " Analyze target structure DATA(lo_target_table) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( ct_target ) ). DATA(lo_target_struct) = CAST cl_abap_structdescr( lo_target_table->get_table_line_type( ) ).
" Analyze source structure DATA(lo_source_table) = CAST cl_abap_tabledescr( cl_abap_typedescr=>describe_by_data( it_source ) ). DATA(lo_source_struct) = CAST cl_abap_structdescr( lo_source_table->get_table_line_type( ) ).
" Determine matching fields DATA(lt_matching) = VALUE string_table( ). LOOP AT lo_target_struct->components INTO DATA(ls_target_comp). READ TABLE lo_source_struct->components WITH KEY name = ls_target_comp-name TRANSPORTING NO FIELDS. IF sy-subrc = 0. APPEND ls_target_comp-name TO lt_matching. ENDIF. ENDLOOP.
" Copy data DATA: lr_target_line TYPE REF TO data. CREATE DATA lr_target_line LIKE LINE OF ct_target. ASSIGN lr_target_line->* TO FIELD-SYMBOL(<fs_target_line>).
LOOP AT it_source ASSIGNING FIELD-SYMBOL(<fs_source_line>). CLEAR <fs_target_line>.
LOOP AT lt_matching INTO DATA(lv_field). ASSIGN COMPONENT lv_field OF STRUCTURE <fs_source_line> TO FIELD-SYMBOL(<fv_source>). ASSIGN COMPONENT lv_field OF STRUCTURE <fs_target_line> TO FIELD-SYMBOL(<fv_target>). <fv_target> = <fv_source>. ENDLOOP.
INSERT <fs_target_line> INTO TABLE ct_target. ENDLOOP. ENDMETHOD.ENDCLASS.13. Dynamic Function Module Calls
DATA: lv_funcname TYPE funcname VALUE 'BAPI_CUSTOMER_GETLIST', lt_params TYPE abap_func_parmbind_tab, lt_excps TYPE abap_func_excpbind_tab.
" Prepare parametersDATA: lt_addressdata TYPE bapi1006_address_t, lt_return TYPE bapiret2_t.
lt_params = VALUE #( ( name = 'ADDRESSDATA' kind = abap_func_tables value = REF #( lt_addressdata ) ) ( name = 'RETURN' kind = abap_func_tables value = REF #( lt_return ) )).
lt_excps = VALUE #( ( name = 'OTHERS' value = 99 )).
" Dynamic callCALL FUNCTION lv_funcname PARAMETER-TABLE lt_params EXCEPTION-TABLE lt_excps.
IF sy-subrc = 0. " SuccessENDIF.14. RTTS for CDS Views
" Analyze CDS View structureDATA(lv_cds_view) = 'ZI_CUSTOMER'.
DATA(lo_cds_type) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( lv_cds_view )).
" Output fieldsLOOP AT lo_cds_type->components INTO DATA(ls_cds_comp). WRITE: / ls_cds_comp-name, ls_cds_comp-type_kind.ENDLOOP.
" Dynamic SELECT on CDS ViewDATA: lr_cds_data TYPE REF TO data.
DATA(lo_cds_table) = cl_abap_tabledescr=>create( lo_cds_type ).CREATE DATA lr_cds_data TYPE HANDLE lo_cds_table.ASSIGN lr_cds_data->* TO FIELD-SYMBOL(<ft_cds_data>).
SELECT * FROM (lv_cds_view) INTO TABLE @<ft_cds_data> UP TO 100 ROWS.15. Generic Report Generator
CLASS zcl_generic_report DEFINITION. PUBLIC SECTION. METHODS: generate_report IMPORTING iv_table TYPE tabname it_fields TYPE string_table iv_where TYPE string OPTIONAL iv_max_rows TYPE i DEFAULT 1000 RETURNING VALUE(rt_output) TYPE string_table.ENDCLASS.
CLASS zcl_generic_report IMPLEMENTATION. METHOD generate_report. DATA: lr_data TYPE REF TO data.
" Create dynamic table DATA(lo_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( iv_table ) ).
" Only desired fields DATA(lt_components) = VALUE cl_abap_structdescr=>component_table( ). LOOP AT it_fields INTO DATA(lv_field). DATA(lo_comp_type) = lo_struct->get_component_type( to_upper( lv_field ) ). IF lo_comp_type IS BOUND. APPEND VALUE #( name = to_upper( lv_field ) type = lo_comp_type ) TO lt_components. ENDIF. ENDLOOP.
IF lt_components IS INITIAL. RETURN. ENDIF.
DATA(lo_new_struct) = cl_abap_structdescr=>create( lt_components ). DATA(lo_new_table) = cl_abap_tabledescr=>create( lo_new_struct ).
CREATE DATA lr_data TYPE HANDLE lo_new_table. ASSIGN lr_data->* TO FIELD-SYMBOL(<ft_data>).
" Read data DATA(lv_fields) = concat_lines_of( table = it_fields sep = ` ` ).
IF iv_where IS INITIAL. SELECT (lv_fields) FROM (iv_table) INTO CORRESPONDING FIELDS OF TABLE @<ft_data> UP TO @iv_max_rows ROWS. ELSE. SELECT (lv_fields) FROM (iv_table) WHERE (iv_where) INTO CORRESPONDING FIELDS OF TABLE @<ft_data> UP TO @iv_max_rows ROWS. ENDIF.
" Create header DATA(lv_header) = concat_lines_of( table = it_fields sep = `|` ). APPEND lv_header TO rt_output.
" Data rows LOOP AT <ft_data> ASSIGNING FIELD-SYMBOL(<fs_row>). DATA(lv_line) = ``.
LOOP AT lt_components INTO DATA(ls_comp). ASSIGN COMPONENT ls_comp-name OF STRUCTURE <fs_row> TO FIELD-SYMBOL(<fv_value>).
IF lv_line IS NOT INITIAL. lv_line = lv_line && `|`. ENDIF. lv_line = lv_line && <fv_value>. ENDLOOP.
APPEND lv_line TO rt_output. ENDLOOP. ENDMETHOD.ENDCLASS.
" UsageDATA(lt_report) = NEW zcl_generic_report( )->generate_report( iv_table = 'KNA1' it_fields = VALUE #( ( `KUNNR` ) ( `NAME1` ) ( `ORT01` ) ( `LAND1` ) ) iv_where = 'LAND1 = ''DE''').
LOOP AT lt_report INTO DATA(lv_line). WRITE: / lv_line.ENDLOOP.Important Notes / Best Practice
- Use RTTS for type information at runtime.
- CREATE DATA TYPE HANDLE for dynamic data objects.
- ASSIGN COMPONENT for field access in dynamic structures.
- Casting with CAST for type conversion of descriptors.
- Consider performance – dynamic code is slower.
- Error handling for non-existent types/fields.
- Avoid SQL injection with dynamic WHERE clauses.
- Dynamic method calls with PARAMETER-TABLE.
- Combine with Field Symbols for efficient processing.
- Use CDS Views for type-safe definitions.