La programacion dinamica permite escribir codigo flexible que se adapta en tiempo de ejecucion a diferentes tipos de datos, estructuras de tablas o requisitos de la aplicacion. ABAP ofrece con RTTS (Run Time Type Services) y CREATE DATA mecanismos potentes para este proposito.
Componentes de RTTS
Clase Descripcion CL_ABAP_TYPEDESCRClase base abstracta CL_ABAP_ELEMDESCRTipos elementales CL_ABAP_STRUCTDESCREstructuras CL_ABAP_TABLEDESCRTablas internas CL_ABAP_REFDESCRTipos de referencia CL_ABAP_CLASSDESCRClases CL_ABAP_INTFDESCRInterfaces
Ejemplos
DATA : lv_string TYPE string VALUE 'Hola' ,
lo_type TYPE REF TO cl_abap_typedescr.
" Obtener descriptor de tipo
lo_type = cl_abap_typedescr=>describe_by_data( lv_string ).
WRITE : / 'Tipo:' , lo_type->type_kind,
/ 'Nombre:' , lo_type->absolute_name,
/ 'Longitud:' , lo_type->length.
2. Analizar estructura
DATA : ls_flight TYPE sflight,
lo_struct TYPE REF TO cl_abap_structdescr,
lt_components TYPE abap_compdescr_tab.
" Obtener descriptor de estructura
lo_struct ?= cl_abap_typedescr=>describe_by_data( ls_flight ).
lt_components = lo_struct->components.
LOOP AT lt_components INTO DATA (ls_comp).
WRITE : / ls_comp-name, ls_comp-type_kind, ls_comp-length.
3. Crear datos dinamicamente
DATA : lr_data TYPE REF TO data .
FIELD-SYMBOLS : <fs_table> TYPE STANDARD TABLE .
" Crear tabla dinamica segun tipo de base de datos
CREATE DATA lr_data TYPE TABLE OF sflight.
ASSIGN lr_data->* TO <fs_table>.
WRITE : / 'Registros leidos:' , lines ( <fs_table> ).
4. Creacion de tipo dinamica con TYPE HANDLE
DATA : lo_elem TYPE REF TO cl_abap_elemdescr,
lo_struct TYPE REF TO cl_abap_structdescr,
lo_table TYPE REF TO cl_abap_tabledescr,
lt_comp TYPE cl_abap_structdescr=>component_table,
lr_data TYPE REF TO data .
FIELD-SYMBOLS : <fs_table> TYPE STANDARD TABLE .
" Definir componentes de estructura
( name = 'CARRID' type = cl_abap_elemdescr=>get_c( 3 ) )
( name = 'CONNID' type = cl_abap_elemdescr=>get_n( 4 ) )
( name = 'FLDATE' type = cl_abap_elemdescr=>get_d( ) )
( name = 'PRICE' type = cl_abap_elemdescr=>get_p( p_length = 8 p_decimals = 2 ) )
lo_struct = cl_abap_structdescr=>create( lt_comp ).
lo_table = cl_abap_tabledescr=>create(
p_table_kind = cl_abap_tabledescr=>tablekind_std
CREATE DATA lr_data TYPE HANDLE lo_table.
ASSIGN lr_data->* TO <fs_table>.
5. Acceso dinamico a componentes de estructura
DATA : ls_person TYPE ty_person,
lv_field TYPE string VALUE 'NAME' .
FIELD-SYMBOLS : <fs_value> TYPE any .
ls_person = VALUE #( id = 1 name = 'Muller' city = 'Berlin' ).
" Acceso dinamico al campo
ASSIGN COMPONENT lv_field OF STRUCTURE ls_person TO <fs_value>.
WRITE : / 'Valor de' , lv_field, ':' , <fs_value>.
6. Crear estructuras y llenar dinamicamente
METHOD create_dynamic_structure .
DATA : lt_comp TYPE cl_abap_structdescr=>component_table,
lo_struct TYPE REF TO cl_abap_structdescr,
lr_data TYPE REF TO data .
FIELD-SYMBOLS : <fs_struct> TYPE any ,
" Obtener campos de la tabla de base de datos
DATA (lt_fields) = get_table_fields( iv_tabname ).
" Crear componentes dinamicamente
LOOP AT lt_fields INTO DATA (ls_field).
name = ls_field-fieldname
type = get_type_for_field( ls_field )
" Crear y usar estructura
lo_struct = cl_abap_structdescr=>create( lt_comp ).
CREATE DATA lr_data TYPE HANDLE lo_struct.
ASSIGN lr_data->* TO <fs_struct>.
LOOP AT lt_fields INTO ls_field.
ASSIGN COMPONENT ls_field-fieldname OF STRUCTURE <fs_struct>
<fs_field> = ls_field-default_value.
7. Copiar estructura a estructura diferente
METHOD copy_matching_components .
DATA : lo_source TYPE REF TO cl_abap_structdescr,
lo_target TYPE REF TO cl_abap_structdescr.
FIELD-SYMBOLS : <fs_source_val> TYPE any ,
<fs_target_val> TYPE any .
lo_source ?= cl_abap_typedescr=>describe_by_data( is_source ).
lo_target ?= cl_abap_typedescr=>describe_by_data( cs_target ).
" Copiar campos coincidentes
LOOP AT lo_source->components INTO DATA (ls_comp).
" Verificar si el componente existe en el destino
READ TABLE lo_target->components
WITH KEY name = ls_comp-name
ASSIGN COMPONENT ls_comp-name OF STRUCTURE is_source TO <fs_source_val>.
ASSIGN COMPONENT ls_comp-name OF STRUCTURE cs_target TO <fs_target_val>.
<fs_target_val> = <fs_source_val>.
8. Acceso dinamico a tabla de base de datos
DATA : lv_tabname TYPE tabname VALUE 'SFLIGHT' ,
lr_data TYPE REF TO data .
FIELD-SYMBOLS : <fs_table> TYPE STANDARD TABLE .
" Crear tabla segun nombre de la tabla de base de datos
CREATE DATA lr_data TYPE TABLE OF (lv_tabname).
ASSIGN lr_data->* TO <fs_table>.
SELECT * FROM (lv_tabname)
WRITE : / 'Registros de' , lv_tabname, ':' , lines ( <fs_table> ).
9. Llamada a metodo dinamica
DATA : lo_object TYPE REF TO object ,
lv_classname TYPE seoclsname VALUE 'CL_MY_CLASS' ,
lv_method TYPE seocpdname VALUE 'GET_DATA' ,
lt_params TYPE abap_parmbind_tab,
lr_result TYPE REF TO data .
" Crear instancia dinamicamente
CREATE OBJECT lo_object TYPE (lv_classname).
kind = cl_abap_objectdescr=>exporting
kind = cl_abap_objectdescr=>importing
value = REF #( lt_result ) )
CALL METHOD lo_object->(lv_method)
PARAMETER-TABLE lt_params.
10. Acceso a tabla con clave dinamica
METHOD read_with_dynamic_key .
DATA : lt_key TYPE abap_keydescr_tab.
FIELD-SYMBOLS : <fs_result> TYPE any .
" Construir clave dinamicamente
( name = 'CARRID' value = REF #( iv_carrid ) )
( name = 'CONNID' value = REF #( iv_connid ) )
" Leer con clave dinamica
ASSIGN it_table[ KEY (lt_key) ] TO <fs_result>.
11. Crear ALV dinamicamente
METHOD create_dynamic_alv .
DATA : lr_table TYPE REF TO data ,
lr_line TYPE REF TO data ,
lo_alv TYPE REF TO cl_salv_table.
FIELD-SYMBOLS : <fs_table> TYPE STANDARD TABLE ,
CREATE DATA lr_table TYPE TABLE OF (iv_tabname).
ASSIGN lr_table->* TO <fs_table>.
SELECT * FROM (iv_tabname)
IMPORTING r_salv_table = lo_alv
CHANGING t_table = <fs_table>
12. JSON a estructura dinamica
METHOD json_to_structure .
DATA : lo_struct TYPE REF TO cl_abap_structdescr,
lt_comp TYPE cl_abap_structdescr=>component_table,
lr_data TYPE REF TO data .
FIELD-SYMBOLS : <fs_struct> TYPE any .
" Analizar JSON para determinar campos
DATA (lt_fields) = parse_json_fields( iv_json ).
" Crear componentes para cada campo JSON
LOOP AT lt_fields INTO DATA (ls_field).
name = to_upper ( ls_field-name )
WHEN ls_field-type = 'string' THEN cl_abap_elemdescr=>get_string( )
WHEN ls_field-type = 'number' THEN cl_abap_elemdescr=>get_p( p_decimals = 2 )
WHEN ls_field-type = 'boolean' THEN cl_abap_elemdescr=>get_c( 1 )
ELSE cl_abap_elemdescr=>get_string( )
lo_struct = cl_abap_structdescr=>create( lt_comp ).
CREATE DATA lr_data TYPE HANDLE lo_struct.
ASSIGN lr_data->* TO <fs_struct>.
/ui2/cl_json=>deserialize(
CHANGING data = <fs_struct>
13. Clausulas WHERE dinamicas
METHOD select_with_dynamic_where .
DATA : lv_where TYPE string .
FIELD-SYMBOLS : <fs_table> TYPE STANDARD TABLE .
" Construir clausula WHERE
LOOP AT it_conditions INTO DATA (ls_cond).
IF lv_where IS NOT INITIAL .
lv_where = lv_where && ' AND ' .
|{ ls_cond-field } { ls_cond-operator } ' { ls_cond-value } ' | .
CREATE DATA er_data TYPE TABLE OF (iv_tabname).
ASSIGN er_data->* TO <fs_table>.
SELECT * FROM (iv_tabname)
14. Reflexion de clases
DATA : lo_class TYPE REF TO cl_abap_classdescr,
lt_methods TYPE abap_methdescr_tab,
lt_attribs TYPE abap_attrdescr_tab.
" Obtener descriptor de clase
lo_class ?= cl_abap_typedescr=>describe_by_name( iv_classname ).
lt_methods = lo_class->methods.
WRITE : / 'Metodos de' , iv_classname.
LOOP AT lt_methods INTO DATA (ls_method).
WRITE : / '-' , ls_method-name,
'Visibilidad:' , ls_method-visibility.
lt_attribs = lo_class->attributes.
LOOP AT lt_attribs INTO DATA (ls_attr).
WRITE : / '-' , ls_attr-name,
'Tipo:' , ls_attr-type_kind.
15. Procesamiento de tablas generico
CLASS lcl_generic_processor DEFINITION .
IMPORTING it_data TYPE ANY TABLE .
CLASS lcl_generic_processor IMPLEMENTATION .
METHOD process_any_table .
DATA : lo_table TYPE REF TO cl_abap_tabledescr,
lo_struct TYPE REF TO cl_abap_structdescr.
FIELD-SYMBOLS : <fs_line> TYPE any ,
" Obtener informacion de tipo
lo_table ?= cl_abap_typedescr=>describe_by_data( it_data ).
lo_struct ?= lo_table->get_table_line_type( ).
LOOP AT it_data ASSIGNING <fs_line>.
WRITE : / '--- Linea' , sy-tabix, '---' .
" Procesar cada componente
LOOP AT lo_struct->components INTO DATA (ls_comp).
ASSIGN COMPONENT ls_comp-name OF STRUCTURE <fs_line>
WRITE : / ls_comp-name, '=' , <fs_field>.
Resumen
Tecnica Caso de uso CL_ABAP_TYPEDESCR=>describe_by_*Obtener informacion de tipo CREATE DATA ... TYPE HANDLECrear datos con tipo dinamico CREATE OBJECT ... TYPE (name)Instanciacion dinamica ASSIGN COMPONENTAcceso a campo dinamico CALL METHOD ... PARAMETER-TABLELlamada a metodo dinamica SELECT ... FROM (tabname)Acceso a tabla de BD dinamico
Notas importantes / Mejores practicas
Usar programacion dinamica solo cuando sea necesario - el codigo estatico es mas rapido y seguro en tipo.
Verificar siempre sy-subrc despues de ASSIGN y operaciones dinamicas.
Las operaciones dinamicas no se pueden verificar en sintaxis - los errores ocurren en tiempo de ejecucion.
Usar Type Handles (TYPE HANDLE) para crear tipos complejos en tiempo de ejecucion.
Los Field-Symbols genericos (TYPE any) son esenciales para programacion dinamica.
El rendimiento es peor que con codigo estatico - considerar almacenamiento en cache si es posible.
Documentar bien el codigo dinamico - es mas dificil de entender.
En ABAP Cloud : las tecnicas dinamicas estan parcialmente restringidas.