Dynamic Programming ermöglicht die Erstellung und Manipulation von Datenobjekten zur Laufzeit. Mit RTTS (Runtime Type Services) und dynamischen Anweisungen können flexible, generische Programme entwickelt werden.
RTTS-Klassen
| Klasse | Beschreibung |
|---|---|
CL_ABAP_TYPEDESCR | Basisklasse für Typbeschreibungen |
CL_ABAP_ELEMDESCR | Elementare Typen (i, string, etc.) |
CL_ABAP_STRUCTDESCR | Strukturen |
CL_ABAP_TABLEDESCR | Interne Tabellen |
CL_ABAP_REFDESCR | Referenzen |
CL_ABAP_CLASSDESCR | Klassen |
CL_ABAP_OBJECTDESCR | Objekte |
Beispiele
1. Typ eines Datenobjekts ermitteln
DATA: lv_string TYPE string VALUE 'Test', lt_table TYPE TABLE OF mara, lo_struct TYPE REF TO kna1.
" Typ ermittelnDATA(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 Typ:', lo_type1->type_kind, / 'Tabelle Typ:', lo_type2->type_kind, / 'KNA1 Typ:', lo_type3->type_kind.
" type_kind Werte:" C = Character, N = Numeric, I = Integer, P = Packed" D = Date, T = Time, X = Hex, F = Float" g = String, h = Table, u = Structure, v = Class2. Strukturkomponenten lesen
DATA: ls_customer TYPE kna1.
" Struktur beschreibenDATA(lo_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_data( ls_customer )).
" Alle Komponenten ausgebenLOOP AT lo_struct->components INTO DATA(ls_comp). WRITE: / ls_comp-name, ls_comp-type_kind, ls_comp-length.ENDLOOP.
" Bestimmte Komponente suchenREAD TABLE lo_struct->components INTO ls_comp WITH KEY name = 'KUNNR'.
IF sy-subrc = 0. WRITE: / 'KUNNR gefunden, Länge:', ls_comp-length.ENDIF.3. Dynamische Struktur erstellen
" Komponenten definierenDATA(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( ) )).
" Strukturtyp erstellenDATA(lo_struct_type) = cl_abap_structdescr=>create( lt_components ).
" Datenobjekt erstellenDATA: lr_struct TYPE REF TO data.CREATE DATA lr_struct TYPE HANDLE lo_struct_type.
" Mit Daten füllenASSIGN lr_struct->* TO FIELD-SYMBOL(<fs_struct>).ASSIGN COMPONENT 'FIELD1' OF STRUCTURE <fs_struct> TO FIELD-SYMBOL(<fv_field1>).<fv_field1> = 'Hallo'.
ASSIGN COMPONENT 'FIELD2' OF STRUCTURE <fs_struct> TO FIELD-SYMBOL(<fv_field2>).<fv_field2> = 42.4. Dynamische interne Tabelle erstellen
" Strukturtyp (wie oben)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 ).
" Tabellentyp erstellenDATA(lo_table_type) = cl_abap_tabledescr=>create( p_line_type = lo_struct_type p_table_kind = cl_abap_tabledescr=>tablekind_std).
" Tabelle erstellenDATA: lr_table TYPE REF TO data.CREATE DATA lr_table TYPE HANDLE lo_table_type.
ASSIGN lr_table->* TO FIELD-SYMBOL(<ft_table>).
" Zeile hinzufügenDATA: 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. Dynamisches 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.
" Tabellenstruktur ermittelnDATA(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( ).
" Dynamische Tabelle erstellenDATA(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>).
" Dynamisches SELECTSELECT (lv_fields) FROM (lv_tablename) WHERE (lv_where) INTO CORRESPONDING FIELDS OF TABLE @<ft_result> UP TO 100 ROWS.
" Ergebnis verarbeitenLOOP 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. Generische Feldliste
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. " Feldliste als String DATA(lv_fields) = concat_lines_of( table = it_fields sep = ` ` ).
" Struktur nur mit gewünschten Feldern erstellen 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.
" Dynamische Tabelle erstellen 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.
" VerwendungDATA: 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. Dynamische Objektinstanziierung
DATA: lv_classname TYPE seoclsname VALUE 'ZCL_MY_CLASS', lo_object TYPE REF TO object.
" Klasse dynamisch instanziierenCREATE OBJECT lo_object TYPE (lv_classname).
" Mit ParameternDATA: lt_params TYPE abap_parmbind_tab.
lt_params = VALUE #( ( name = 'IV_PARAM1' kind = cl_abap_objectdescr=>exporting value = REF #( 'Wert1' ) ) ( name = 'IV_PARAM2' kind = cl_abap_objectdescr=>exporting value = REF #( 123 ) )).
CREATE OBJECT lo_object TYPE (lv_classname) PARAMETER-TABLE lt_params.8. Dynamischer Methodenaufruf
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.
" Objekt erstellenCREATE OBJECT lo_object TYPE zcl_processor.
" Parameter aufbauenlt_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 ) )).
" Methode aufrufenCALL METHOD lo_object->(lv_method) PARAMETER-TABLE lt_params EXCEPTION-TABLE lt_results.
WRITE: / 'Ergebnis:', lv_output.9. Klassenmethoden und Attribute analysieren
DATA: lv_classname TYPE seoclsname VALUE 'CL_ABAP_TYPEDESCR'.
" Klassenbeschreibung ladenDATA(lo_class) = CAST cl_abap_classdescr( cl_abap_typedescr=>describe_by_name( lv_classname )).
" Methoden auflistenWRITE: / 'Methoden:'.LOOP AT lo_class->methods INTO DATA(ls_method). WRITE: / '-', ls_method-name, ls_method-visibility.ENDLOOP.
" Attribute auflistenWRITE: / 'Attribute:'.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. Generische Datenkonvertierung
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. " Struktur analysieren 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( ) ).
" Jede Zeile in String konvertieren 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. " Einfache JSON-Konvertierung 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. Dynamische WHERE-Klausel erstellen
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.
" VerwendungDATA(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( ).
" Ergebnis: LAND1 = 'DE' AND KTOKD = '0001'12. Generische Tabellenkopie
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. " Zielstruktur analysieren 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( ) ).
" Quellstruktur analysieren 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( ) ).
" Matching Felder ermitteln 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.
" Daten kopieren 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. Dynamische Funktionsbausteinaufrufe
DATA: lv_funcname TYPE funcname VALUE 'BAPI_CUSTOMER_GETLIST', lt_params TYPE abap_func_parmbind_tab, lt_excps TYPE abap_func_excpbind_tab.
" Parameter vorbereitenDATA: 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 )).
" Dynamischer AufrufCALL FUNCTION lv_funcname PARAMETER-TABLE lt_params EXCEPTION-TABLE lt_excps.
IF sy-subrc = 0. " ErfolgENDIF.14. RTTS für CDS Views
" CDS View Struktur analysierenDATA(lv_cds_view) = 'ZI_CUSTOMER'.
DATA(lo_cds_type) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( lv_cds_view )).
" Felder ausgebenLOOP AT lo_cds_type->components INTO DATA(ls_cds_comp). WRITE: / ls_cds_comp-name, ls_cds_comp-type_kind.ENDLOOP.
" Dynamisch SELECT auf 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. Generischer 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.
" Dynamische Tabelle erstellen DATA(lo_struct) = CAST cl_abap_structdescr( cl_abap_typedescr=>describe_by_name( iv_table ) ).
" Nur gewünschte Felder 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>).
" Daten lesen 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.
" Header erstellen DATA(lv_header) = concat_lines_of( table = it_fields sep = `|` ). APPEND lv_header TO rt_output.
" Datenzeilen 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.
" VerwendungDATA(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.Wichtige Hinweise / Best Practice
- RTTS für Typinformationen zur Laufzeit nutzen.
- CREATE DATA TYPE HANDLE für dynamische Datenobjekte.
- ASSIGN COMPONENT für Feldzugriff in dynamischen Strukturen.
- Casting mit CAST für Typkonvertierung von Deskriptoren.
- Performance beachten – dynamischer Code ist langsamer.
- Fehlerbehandlung bei nicht existierenden Typen/Feldern.
- SQL Injection vermeiden bei dynamischen WHERE-Klauseln.
- Dynamische Methodenaufrufe mit PARAMETER-TABLE.
- Kombinieren Sie mit Field Symbols für effiziente Verarbeitung.
- Verwenden Sie CDS Views für typsichere Definitionen.