Fluent builder around RTTS (CL_ABAP_STRUCTDESCR, CL_ABAP_TABLEDESCR,
CL_ABAP_ELEMDESCR) for creating dynamic ABAP types at runtime.
Removes the boilerplate of manually building a component table and calling the RTTS APIs directly, replacing it with a readable chain syntax.
The class does one thing only: describe a type. It does not allocate, read, or write data. The typical lifecycle is:
NEW lcl_dynamic_type( )
->add_field( ... ) " 0..n times
->add_field_like( ... ) " 0..n times
->build_struct / build_line / build_table
add_field* methods return me (method chaining). build_* methods are
terminal: they return the result and break the chain.
Adds a field whose type is resolved via CL_ABAP_ELEMDESCR.
| Parameter | Type | Required | Description |
|---|---|---|---|
iv_name |
string |
yes | Field name in the structure (e.g. 'MATNR') |
iv_type |
string |
yes | Primitive ABAP type (see table below) |
iv_length |
i |
no | Length in characters / bytes / digits (used by C, N, X, P) |
iv_decimals |
i |
no | Number of decimal places (used by P only) |
Valid values for iv_type:
| Value | ABAP type | iv_length |
iv_decimals |
Default length |
|---|---|---|---|---|
'C' |
Character | yes | - | 1 |
'N' |
Numeric text | yes | - | 1 |
'X' |
Hexadecimal | yes | - | 1 |
'I' |
Integer | - | - | (fixed) |
'INT8' |
8-byte integer | - | - | (fixed) |
'F' |
Float | - | - | (fixed) |
'D' |
Date | - | - | (fixed) |
'T' |
Time | - | - | (fixed) |
'P' |
Packed | yes | yes | 8 / 0 |
'STRING' |
String | - | - | (dynamic) |
'XSTRING' |
Byte string | - | - | (dynamic) |
| other | DDIC fallback | - | - | — |
Returns REF TO lcl_dynamic_type (me) for chaining.
Adds a field whose type is read from the ABAP Dictionary via
CL_ABAP_TYPEDESCR=>DESCRIBE_BY_NAME. Use this to inherit domain
properties (length, conversion exit, search help) from standard DDIC types
such as MATNR, WERKS_D, MENGE_D, etc.
| Parameter | Type | Required | Description |
|---|---|---|---|
iv_name |
string |
yes | Field name in the structure |
iv_ddic_type |
string |
yes | DDIC type name (e.g. 'MATNR', 'MENGE_D', 'WERKS_D') |
Returns REF TO lcl_dynamic_type (me) for chaining.
Terminal method. Calls CL_ABAP_STRUCTDESCR=>CREATE with the accumulated
component table and returns the structure descriptor.
| Parameter | Type | Description |
|---|---|---|
ro_desc |
REF TO cl_abap_structdescr |
Descriptor of the built structure |
Use this when you need to pass the type descriptor to another RTTS API or inspect the components before allocating memory.
Terminal method. Calls build_struct internally, then allocates a single
empty row via CREATE DATA ... TYPE HANDLE.
| Parameter | Type | Description |
|---|---|---|
ro_data |
REF TO data |
Pointer to an empty structure row |
To access individual fields use a field symbol with ASSIGN COMPONENT ... OF STRUCTURE.
Terminal method. Calls build_struct, wraps it in a CL_ABAP_TABLEDESCR
(standard table, default key, non-unique), then allocates an empty internal
table via CREATE DATA ... TYPE HANDLE.
| Parameter | Type | Description |
|---|---|---|
ro_table |
REF TO data |
Pointer to an empty internal table |
To pass the result to CL_SALV_TABLE=>FACTORY use a field symbol first:
ASSIGN lr_table->* TO FIELD-SYMBOL(<lt_table>).This is required because CHANGING t_table TYPE ANY TABLE needs a
runtime-resolved type, not an inline-dereferenced REF TO DATA.
DATA(lr_table) = NEW lcl_dynamic_type(
)->add_field( iv_name = 'MATNR' iv_type = 'C' iv_length = 18
)->add_field( iv_name = 'MENGE' iv_type = 'P' iv_length = 13 iv_decimals = 3
)->add_field( iv_name = 'WERKS' iv_type = 'C' iv_length = 4
)->add_field( iv_name = 'BUDAT' iv_type = 'D'
)->build_table( ).
" Assign to a field symbol to satisfy CHANGING ANY TABLE
ASSIGN lr_table->* TO FIELD-SYMBOL(<lt_table>).
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(lo_salv)
CHANGING t_table = <lt_table> ).
lo_salv->display( ).DATA(lr_table) = NEW lcl_dynamic_type(
)->add_field_like( iv_name = 'MATNR' iv_ddic_type = 'MATNR'
)->add_field_like( iv_name = 'WERKS' iv_ddic_type = 'WERKS_D'
)->add_field_like( iv_name = 'MENGE' iv_ddic_type = 'MENGE_D'
)->add_field( iv_name = 'NOTE' iv_type = 'STRING'
)->build_table( ).DATA(lr_line) = NEW lcl_dynamic_type(
)->add_field( iv_name = 'ID' iv_type = 'I'
)->add_field( iv_name = 'NAME' iv_type = 'C' iv_length = 40
)->build_line( ).
ASSIGN lr_line->* TO FIELD-SYMBOL(<ls_line>).
ASSIGN COMPONENT 'NAME' OF STRUCTURE <ls_line> TO FIELD-SYMBOL(<lv_name>).
<lv_name> = 'John Doe'.DATA(lo_struct_desc) = NEW lcl_dynamic_type(
)->add_field( iv_name = 'KUNNR' iv_type = 'C' iv_length = 10
)->add_field( iv_name = 'NETWR' iv_type = 'P' iv_length = 15 iv_decimals = 2
)->build_struct( ).
" Inspect the generated components
LOOP AT lo_struct_desc->components ASSIGNING FIELD-SYMBOL(<ls_comp>).
WRITE: / <ls_comp>-name.
ENDLOOP.DATA(lr_table) = NEW lcl_dynamic_type(
)->add_field_like( iv_name = 'VBELN' iv_ddic_type = 'VBELN_VA'
)->add_field_like( iv_name = 'POSNR' iv_ddic_type = 'POSNV'
)->add_field( iv_name = 'QTY' iv_type = 'P' iv_length = 13 iv_decimals = 3
)->add_field( iv_name = 'FLAG' iv_type = 'C' iv_length = 1
)->build_table( ).- The class does not manage data: there are no methods to read or write
field values. Use field symbols and
ASSIGN COMPONENTfor that. - Each instance keeps its own component list. To build two different types, use two separate instances.
add_fieldalso accepts DDIC type names iniv_type(falls back toDESCRIBE_BY_NAME), but usingadd_field_likeis clearer in that case.- The table produced by
build_tableis always standard with a default non-unique key. For sorted or hashed tables, callbuild_structdirectly and pass the descriptor toCL_ABAP_TABLEDESCR=>CREATEwith the desired parameters.
The following program is self-contained: it includes the full class definition
and a START-OF-SELECTION block that builds a dynamic table, populates it
with two rows, and displays it via ALV.
REPORT z_dynamic_type_demo.
*----------------------------------------------------------------------*
* lcl_dynamic_type — fluent builder around RTTS
*----------------------------------------------------------------------*
CLASS lcl_dynamic_type DEFINITION.
PUBLIC SECTION.
METHODS add_field
IMPORTING
iv_name TYPE string
iv_type TYPE string
iv_length TYPE i OPTIONAL
iv_decimals TYPE i OPTIONAL
RETURNING
VALUE(ro_self) TYPE REF TO lcl_dynamic_type.
METHODS add_field_like
IMPORTING
iv_name TYPE string
iv_ddic_type TYPE string
RETURNING
VALUE(ro_self) TYPE REF TO lcl_dynamic_type.
METHODS build_struct
RETURNING
VALUE(ro_desc) TYPE REF TO cl_abap_structdescr.
METHODS build_line
RETURNING
VALUE(ro_data) TYPE REF TO data.
METHODS build_table
RETURNING
VALUE(ro_table) TYPE REF TO data.
PRIVATE SECTION.
DATA mt_components TYPE cl_abap_structdescr=>component_table.
METHODS get_elem_descr
IMPORTING
iv_type TYPE string
iv_length TYPE i OPTIONAL
iv_decimals TYPE i OPTIONAL
RETURNING
VALUE(ro_elem) TYPE REF TO cl_abap_elemdescr.
ENDCLASS.
CLASS lcl_dynamic_type IMPLEMENTATION.
METHOD add_field.
DATA ls_comp TYPE cl_abap_structdescr=>component.
ls_comp-name = iv_name.
ls_comp-type = get_elem_descr(
iv_type = iv_type
iv_length = iv_length
iv_decimals = iv_decimals ).
APPEND ls_comp TO mt_components.
ro_self = me.
ENDMETHOD.
METHOD add_field_like.
DATA ls_comp TYPE cl_abap_structdescr=>component.
ls_comp-name = iv_name.
ls_comp-type ?= cl_abap_typedescr=>describe_by_name( iv_ddic_type ).
APPEND ls_comp TO mt_components.
ro_self = me.
ENDMETHOD.
METHOD build_struct.
ro_desc = cl_abap_structdescr=>create( mt_components ).
ENDMETHOD.
METHOD build_line.
DATA(lo_struct) = build_struct( ).
CREATE DATA ro_data TYPE HANDLE lo_struct.
ENDMETHOD.
METHOD build_table.
DATA(lo_struct) = build_struct( ).
DATA(lo_tabledesc) = cl_abap_tabledescr=>create(
p_line_type = lo_struct
p_table_kind = cl_abap_tabledescr=>tablekind_std
p_unique = abap_false ).
CREATE DATA ro_table TYPE HANDLE lo_tabledesc.
ENDMETHOD.
METHOD get_elem_descr.
CASE to_upper( iv_type ).
WHEN 'C'.
ro_elem = cl_abap_elemdescr=>get_c( p_length = COND #( WHEN iv_length > 0 THEN iv_length ELSE 1 ) ).
WHEN 'N'.
ro_elem = cl_abap_elemdescr=>get_n( p_length = COND #( WHEN iv_length > 0 THEN iv_length ELSE 1 ) ).
WHEN 'X'.
ro_elem = cl_abap_elemdescr=>get_x( p_length = COND #( WHEN iv_length > 0 THEN iv_length ELSE 1 ) ).
WHEN 'I'.
ro_elem = cl_abap_elemdescr=>get_i( ).
WHEN 'INT8'.
ro_elem = cl_abap_elemdescr=>get_int8( ).
WHEN 'F'.
ro_elem = cl_abap_elemdescr=>get_f( ).
WHEN 'D'.
ro_elem = cl_abap_elemdescr=>get_d( ).
WHEN 'T'.
ro_elem = cl_abap_elemdescr=>get_t( ).
WHEN 'P'.
ro_elem = cl_abap_elemdescr=>get_p(
p_length = COND #( WHEN iv_length > 0 THEN iv_length ELSE 8 )
p_decimals = COND #( WHEN iv_decimals > 0 THEN iv_decimals ELSE 0 ) ).
WHEN 'STRING'.
ro_elem = cl_abap_elemdescr=>get_string( ).
WHEN 'XSTRING'.
ro_elem = cl_abap_elemdescr=>get_xstring( ).
WHEN OTHERS.
ro_elem ?= cl_abap_typedescr=>describe_by_name( iv_type ).
ENDCASE.
ENDMETHOD.
ENDCLASS.
*----------------------------------------------------------------------*
* Main program
*----------------------------------------------------------------------*
START-OF-SELECTION.
" Declare all field symbols upfront with explicit types
FIELD-SYMBOLS: <lt_table> TYPE STANDARD TABLE,
<ls_row> TYPE ANY,
<lv_val> TYPE ANY.
" 1. Build the type and allocate the table
" MATNR and WERKS come from the DDIC — column labels are automatic.
" MENGE and BUDAT are primitive types — labels must be set manually.
DATA(lr_table) = NEW lcl_dynamic_type(
)->add_field_like( iv_name = 'MATNR' iv_ddic_type = 'MATNR'
)->add_field_like( iv_name = 'WERKS' iv_ddic_type = 'WERKS_D'
)->add_field( iv_name = 'MENGE' iv_type = 'P' iv_length = 13 iv_decimals = 3
)->add_field( iv_name = 'BUDAT' iv_type = 'D'
)->build_table( ).
ASSIGN lr_table->* TO <lt_table>.
" 2. Populate the table row by row
DO 2 TIMES.
APPEND INITIAL LINE TO <lt_table> ASSIGNING <ls_row>.
ASSIGN COMPONENT 'MATNR' OF STRUCTURE <ls_row> TO <lv_val>.
<lv_val> = COND #( WHEN sy-index = 1 THEN 'MATERIAL-001'
ELSE 'MATERIAL-002' ).
ASSIGN COMPONENT 'WERKS' OF STRUCTURE <ls_row> TO <lv_val>.
<lv_val> = '1000'.
ASSIGN COMPONENT 'MENGE' OF STRUCTURE <ls_row> TO <lv_val>.
<lv_val> = COND #( WHEN sy-index = 1 THEN '42.500' ELSE '10.000' ).
ASSIGN COMPONENT 'BUDAT' OF STRUCTURE <ls_row> TO <lv_val>.
<lv_val> = sy-datum.
ENDDO.
" 3. Display with ALV
DATA lo_salv TYPE REF TO cl_salv_table.
cl_salv_table=>factory(
IMPORTING r_salv_table = lo_salv
CHANGING t_table = <lt_table> ).
" Only primitive-type fields need manual labels.
DATA(lo_cols) = lo_salv->get_columns( ).
lo_cols->get_column( 'MENGE' )->set_long_text( 'Quantity' ).
lo_cols->get_column( 'BUDAT' )->set_long_text( 'Posting Date' ).
lo_cols->set_optimize( abap_true ).
lo_salv->display( ).