Programacion Dinamica en ABAP: RTTS, CREATE DATA y Reflexion

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

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

ClaseDescripcion
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

1. Determinar informacion de tipo

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 ).
" Obtener componentes
lt_components = lo_struct->components.
LOOP AT lt_components INTO DATA(ls_comp).
WRITE: / ls_comp-name, ls_comp-type_kind, ls_comp-length.
ENDLOOP.

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>.
" Llenar dinamicamente
SELECT * FROM sflight
INTO TABLE <fs_table>
UP TO 10 ROWS.
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
lt_comp = VALUE #(
( 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 ) )
).
" Crear estructura
lo_struct = cl_abap_structdescr=>create( lt_comp ).
" Crear tabla
lo_table = cl_abap_tabledescr=>create(
p_line_type = lo_struct
p_table_kind = cl_abap_tabledescr=>tablekind_std
).
" Crear datos
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>.
IF sy-subrc = 0.
WRITE: / 'Valor de', lv_field, ':', <fs_value>.
ENDIF.

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,
<fs_field> 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).
APPEND VALUE #(
name = ls_field-fieldname
type = get_type_for_field( ls_field )
) TO lt_comp.
ENDLOOP.
" 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>.
" Llenar dinamicamente
LOOP AT lt_fields INTO ls_field.
ASSIGN COMPONENT ls_field-fieldname OF STRUCTURE <fs_struct>
TO <fs_field>.
IF sy-subrc = 0.
<fs_field> = ls_field-default_value.
ENDIF.
ENDLOOP.
ENDMETHOD.

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.
" Descriptores
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
TRANSPORTING NO FIELDS.
IF sy-subrc = 0.
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>.
IF sy-subrc = 0.
<fs_target_val> = <fs_source_val>.
ENDIF.
ENDIF.
ENDLOOP.
ENDMETHOD.

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 dinamico
SELECT * FROM (lv_tabname)
INTO TABLE <fs_table>
UP TO 100 ROWS.
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).
" Preparar parametros
lt_params = VALUE #(
( name = 'IV_ID'
kind = cl_abap_objectdescr=>exporting
value = REF #( lv_id ) )
( name = 'ET_RESULT'
kind = cl_abap_objectdescr=>importing
value = REF #( lt_result ) )
).
" Llamar dinamicamente
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
lt_key = VALUE #(
( 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>.
IF sy-subrc = 0.
rs_result = <fs_result>.
ENDIF.
ENDMETHOD.

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,
<fs_line> TYPE any.
" Crear tabla dinamica
CREATE DATA lr_table TYPE TABLE OF (iv_tabname).
ASSIGN lr_table->* TO <fs_table>.
" Leer datos
SELECT * FROM (iv_tabname)
INTO TABLE <fs_table>
UP TO iv_max_rows ROWS.
" Crear ALV
cl_salv_table=>factory(
IMPORTING r_salv_table = lo_alv
CHANGING t_table = <fs_table>
).
lo_alv->display( ).
ENDMETHOD.

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).
APPEND VALUE #(
name = to_upper( ls_field-name )
type = COND #(
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( )
)
) TO lt_comp.
ENDLOOP.
" Crear estructura
lo_struct = cl_abap_structdescr=>create( lt_comp ).
CREATE DATA lr_data TYPE HANDLE lo_struct.
ASSIGN lr_data->* TO <fs_struct>.
" Deserializar
/ui2/cl_json=>deserialize(
EXPORTING json = iv_json
CHANGING data = <fs_struct>
).
er_data = lr_data.
ENDMETHOD.

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 '.
ENDIF.
lv_where = lv_where &&
|{ ls_cond-field } { ls_cond-operator } '{ ls_cond-value }'|.
ENDLOOP.
" SELECT dinamico
CREATE DATA er_data TYPE TABLE OF (iv_tabname).
ASSIGN er_data->* TO <fs_table>.
SELECT * FROM (iv_tabname)
WHERE (lv_where)
INTO TABLE <fs_table>.
ENDMETHOD.

14. Reflexion de clases

METHOD get_class_info.
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 ).
" Obtener metodos
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.
ENDLOOP.
" Obtener atributos
lt_attribs = lo_class->attributes.
WRITE: / 'Atributos:'.
LOOP AT lt_attribs INTO DATA(ls_attr).
WRITE: / '-', ls_attr-name,
'Tipo:', ls_attr-type_kind.
ENDLOOP.
ENDMETHOD.

15. Procesamiento de tablas generico

CLASS lcl_generic_processor DEFINITION.
PUBLIC SECTION.
METHODS:
process_any_table
IMPORTING it_data TYPE ANY TABLE.
ENDCLASS.
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,
<fs_field> TYPE any.
" Obtener informacion de tipo
lo_table ?= cl_abap_typedescr=>describe_by_data( it_data ).
lo_struct ?= lo_table->get_table_line_type( ).
" Procesar cada linea
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>
TO <fs_field>.
IF sy-subrc = 0.
WRITE: / ls_comp-name, '=', <fs_field>.
ENDIF.
ENDLOOP.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

Resumen

TecnicaCaso 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.