ABAP Dynamic Programming: RTTS, CREATE DATA, Reflection

Category
ABAP-Statements
Published
Author
Johannes

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

ClassDescription
CL_ABAP_TYPEDESCRBase class for type descriptions
CL_ABAP_ELEMDESCRElementary types (i, string, etc.)
CL_ABAP_STRUCTDESCRStructures
CL_ABAP_TABLEDESCRInternal tables
CL_ABAP_REFDESCRReferences
CL_ABAP_CLASSDESCRClasses
CL_ABAP_OBJECTDESCRObjects

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 type
DATA(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 = Class

2. Read Structure Components

DATA: ls_customer TYPE kna1.
" Describe structure
DATA(lo_struct) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_data( ls_customer )
).
" Output all components
LOOP AT lo_struct->components INTO DATA(ls_comp).
WRITE: / ls_comp-name, ls_comp-type_kind, ls_comp-length.
ENDLOOP.
" Search for specific component
READ 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 components
DATA(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 type
DATA(lo_struct_type) = cl_abap_structdescr=>create( lt_components ).
" Create data object
DATA: lr_struct TYPE REF TO data.
CREATE DATA lr_struct TYPE HANDLE lo_struct_type.
" Fill with data
ASSIGN 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 type
DATA(lo_table_type) = cl_abap_tabledescr=>create(
p_line_type = lo_struct_type
p_table_kind = cl_abap_tabledescr=>tablekind_std
).
" Create table
DATA: lr_table TYPE REF TO data.
CREATE DATA lr_table TYPE HANDLE lo_table_type.
ASSIGN lr_table->* TO FIELD-SYMBOL(<ft_table>).
" Add row
DATA: 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 structure
DATA(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 table
DATA(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 SELECT
SELECT (lv_fields)
FROM (lv_tablename)
WHERE (lv_where)
INTO CORRESPONDING FIELDS OF TABLE @<ft_result>
UP TO 100 ROWS.
" Process result
LOOP 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.
" Usage
DATA: 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 dynamically
CREATE OBJECT lo_object TYPE (lv_classname).
" With parameters
DATA: 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 object
CREATE OBJECT lo_object TYPE zcl_processor.
" Build parameters
lt_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 method
CALL 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 description
DATA(lo_class) = CAST cl_abap_classdescr(
cl_abap_typedescr=>describe_by_name( lv_classname )
).
" List methods
WRITE: / 'Methods:'.
LOOP AT lo_class->methods INTO DATA(ls_method).
WRITE: / '-', ls_method-name, ls_method-visibility.
ENDLOOP.
" List attributes
WRITE: / 'Attributes:'.
LOOP AT lo_class->attributes INTO DATA(ls_attr).
WRITE: / '-', ls_attr-name, ls_attr-type_kind.
ENDLOOP.
" Interfaces
WRITE: / '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.
" Usage
DATA(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 parameters
DATA: 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 call
CALL FUNCTION lv_funcname
PARAMETER-TABLE lt_params
EXCEPTION-TABLE lt_excps.
IF sy-subrc = 0.
" Success
ENDIF.

14. RTTS for CDS Views

" Analyze CDS View structure
DATA(lv_cds_view) = 'ZI_CUSTOMER'.
DATA(lo_cds_type) = CAST cl_abap_structdescr(
cl_abap_typedescr=>describe_by_name( lv_cds_view )
).
" Output fields
LOOP 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 View
DATA: 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.
" Usage
DATA(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.