In my last blog I explained about the significance of field symbol and data references in dynamic programming.
https://saponlineguides.blogspot.in/2017/09/dynamic-programming-in-abap-part-1-introduction-to-field-symbols.html
https://saponlineguides.blogspot.in/2017/09/dynamic-programming-in-abap-part-2-introduction-to-data-reference.html
Now here we will see one example of dynamic programming approach and also a brief introduction to ABAP RTTS.
ABAP Runtime Type Services (RTTS) consists of two components:
◉ Runtime Type Identification (RTTI) – Provides the methods to get the type definition of data objects at runtime.
◉ Runtime Type Creation (RTTC) – Provides the methods to create the data objects at runtime with any type definition.
Basically, ABAP RTTS provides a set of classes, whose methods can be used for runtime type identification and runtime type creation. To know more about ABAP RTTS you can follow below link:
https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=42965
Requirement: As an ABAP developer, very often we get the situation where we need to write data from an internal table to a file on application server.
Solution: We will build one class having a method which will take any internal table as input and write its content in a file on application server.
Class Definition:
CLASS cl_appserver_writer DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: write IMPORTING
iv_filename TYPE string
it_data TYPE ANY TABLE
write_header TYPE abap_bool DEFAULT space
EXPORTING
ev_message TYPE string.
ENDCLASS.
Here importing parameter it_data is of TYPE ANY TABLE so that it can receive any internal table.
Class Implementation:
CLASS cl_appserver_writer IMPLEMENTATION.
METHOD write.
TYPES: BEGIN OF ty_comp_detail,
name TYPE abap_compname,
descr TYPE scrtext_m,
END OF ty_comp_detail.
DATA: lo_type_def TYPE REF TO cl_abap_typedescr.
DATA: lo_struct_def TYPE REF TO cl_abap_structdescr.
DATA: lo_table_def TYPE REF TO cl_abap_tabledescr.
DATA: lo_data_def TYPE REF TO cl_abap_datadescr.
DATA: lo_element_def TYPE REF TO cl_abap_elemdescr.
DATA: lt_components TYPE abap_compdescr_tab.
DATA: wa_components LIKE LINE OF lt_components.
DATA: lv_str TYPE string.
DATA: lv_filerow TYPE string.
DATA: lv_counter TYPE i VALUE 0.
DATA: lw_field_info TYPE dfies.
DATA: ls_comp_detail TYPE ty_comp_detail.
DATA: lt_comp_detail TYPE TABLE OF ty_comp_detail.
FIELD-SYMBOLS: <row> TYPE any.
FIELD-SYMBOLS: <field_value> TYPE any.
* Using RTTS to get the runtime type information of the internal table
lo_type_def = cl_abap_tabledescr=>describe_by_data( it_data ).
lo_table_def ?= lo_type_def.
lo_data_def = lo_table_def->get_table_line_type( ).
lo_struct_def ?= lo_data_def.
* Get the components of the structure
lt_components = lo_struct_def->components.
CLEAR: lo_data_def.
* If the WRITE_HEADER is ABAP_TRUE then fetch the label
* of data element associated to each component of the
* line type structure of internal table, if no data element
* is associated then use component name as the header text
IF write_header EQ abap_true.
LOOP AT lt_components INTO wa_components.
lo_data_def = lo_struct_def->get_component_type( wa_components-name ).
lo_element_def ?= lo_data_def.
lw_field_info = lo_element_def->get_ddic_field( ).
ls_comp_detail-name = lw_field_info-rollname. "Get the data element name
* Calling FM to get data element text
CALL FUNCTION 'WCGW_DATA_ELEMENT_TEXT_GET'
EXPORTING
i_data_element = lw_field_info-rollname
i_language = sy-langu
IMPORTING
e_scrtext_m = ls_comp_detail-descr
EXCEPTIONS
error = 1.
IF ls_comp_detail-descr IS INITIAL.
ls_comp_detail-descr = wa_components-name.
ENDIF.
APPEND ls_comp_detail TO lt_comp_detail.
CLEAR: ls_comp_detail.
ENDLOOP.
ENDIF.
OPEN DATASET iv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
IF sy-subrc EQ 0.
* Writing header text for each column separated by comma
IF write_header EQ abap_true.
LOOP AT lt_comp_detail INTO ls_comp_detail.
lv_counter = lv_counter + 1.
IF lv_counter EQ 1.
lv_filerow = ls_comp_detail-descr.
ELSE.
CONCATENATE lv_filerow ',' ls_comp_detail-descr INTO lv_filerow.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDIF.
* Writing internal table content separated by comma
LOOP AT it_data ASSIGNING <row>.
LOOP AT lt_components INTO wa_components.
lv_counter = lv_counter + 1.
ASSIGN COMPONENT wa_components-name OF STRUCTURE <row> TO <field_value>.
IF <field_value> IS ASSIGNED.
lv_str = <field_value>.
IF lv_counter EQ 1.
lv_filerow = lv_str.
ELSE.
CONCATENATE lv_filerow ',' lv_str INTO lv_filerow.
ENDIF.
UNASSIGN <field_value>.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDLOOP.
CLOSE DATASET iv_filename.
ev_message = 'Success'.
ELSE.
ev_message = 'Failure'.
ENDIF.
ENDMETHOD.
ENDCLASS.
Here the classes CL_ABAP_*DESCR are provided by the ABAP RTTS and used to get the type definition of data objects at runtime. Also we have extracted the data element name of each component of line type structure of internal table it_data using RTTS classes. Then we fetched the data element label using the FM WCGW_DATA_ELEMENT_TEXT_GET. This label is used to write the header for each column of internal table it_data if WRITE_HEADER parameter of class is provided with ABAP_TRUE.
Using the Class – The above designed class can be used as:
DATA: lt_data TYPE STANDARD TABLE OF mara.
DATA: lv_filename TYPE string.
DATA: lv_message TYPE string.
SELECT * FROM mara INTO TABLE lt_data UP TO 5 ROWS.
cl_appserver_writer=>write(
EXPORTING
iv_filename = 'D:\usr\sap\testdata.csv'
it_data = lt_data
write_header = abap_true
IMPORTING
ev_message = lv_message
).
WRITE: / lv_message.
Here we are passing one internal table of structure MARA to the class, and subsequently its content will be written on application server as comma separated values. However, we can pass internal table of any structure. This file can also be downloaded from application server to an excel spreadsheet.
So this is how field symbol, data reference, generic data type, RTTS helps in dynamic programming approach.
App Server Writer.txt
REPORT zwrite_appserver.
CLASS cl_appserver_writer DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: write IMPORTING
iv_filename TYPE string
it_data TYPE ANY TABLE
write_header TYPE abap_bool DEFAULT space
EXPORTING
ev_message TYPE string.
ENDCLASS.
CLASS cl_appserver_writer IMPLEMENTATION.
METHOD write.
TYPES: BEGIN OF ty_comp_detail,
name TYPE abap_compname,
descr TYPE scrtext_m,
END OF ty_comp_detail.
DATA: lo_type_def TYPE REF TO cl_abap_typedescr.
DATA: lo_struct_def TYPE REF TO cl_abap_structdescr.
DATA: lo_table_def TYPE REF TO cl_abap_tabledescr.
DATA: lo_data_def TYPE REF TO cl_abap_datadescr.
DATA: lo_element_def TYPE REF TO cl_abap_elemdescr.
DATA: lt_components TYPE abap_compdescr_tab.
DATA: wa_components LIKE LINE OF lt_components.
DATA: lv_str TYPE string.
DATA: lv_filerow TYPE string.
DATA: lv_counter TYPE i VALUE 0.
DATA: lw_field_info TYPE dfies.
DATA: ls_comp_detail TYPE ty_comp_detail.
DATA: lt_comp_detail TYPE TABLE OF ty_comp_detail.
FIELD-SYMBOLS: <row> TYPE any.
FIELD-SYMBOLS: <field_value> TYPE any.
* Using RTTS to get the runtime type information of the internal table
lo_type_def = cl_abap_tabledescr=>describe_by_data( it_data ).
lo_table_def ?= lo_type_def.
lo_data_def = lo_table_def->get_table_line_type( ).
lo_struct_def ?= lo_data_def.
lt_components = lo_struct_def->components.
CLEAR: lo_data_def.
* If the WRITE_HEADER is ABAP_TRUE then fetch the label
* of data element associated to each component of the
* line type structure of internal table, if no data element
* is associated then use component name as the header text
IF write_header EQ abap_true.
LOOP AT lt_components INTO wa_components.
lo_data_def = lo_struct_def->get_component_type( wa_components-name ).
lo_element_def ?= lo_data_def.
lw_field_info = lo_element_def->get_ddic_field( ).
ls_comp_detail-name = lw_field_info-rollname.
* Calling FM to get data element text
CALL FUNCTION 'WCGW_DATA_ELEMENT_TEXT_GET'
EXPORTING
i_data_element = lw_field_info-rollname
i_language = sy-langu
IMPORTING
e_scrtext_m = ls_comp_detail-descr
EXCEPTIONS
error = 1.
IF ls_comp_detail-descr IS INITIAL.
ls_comp_detail-descr = wa_components-name.
ENDIF.
APPEND ls_comp_detail TO lt_comp_detail.
CLEAR: ls_comp_detail.
ENDLOOP.
ENDIF.
OPEN DATASET iv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
IF sy-subrc EQ 0.
* Writing header text for each column separated by comma
IF write_header EQ abap_true.
LOOP AT lt_comp_detail INTO ls_comp_detail.
lv_counter = lv_counter + 1.
IF lv_counter EQ 1.
lv_filerow = ls_comp_detail-descr.
ELSE.
CONCATENATE lv_filerow ',' ls_comp_detail-descr INTO lv_filerow.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDIF.
* Writing internal table content separated by comma
LOOP AT it_data ASSIGNING <row>.
LOOP AT lt_components INTO wa_components.
lv_counter = lv_counter + 1.
ASSIGN COMPONENT wa_components-name OF STRUCTURE <row> TO <field_value>.
IF <field_value> IS ASSIGNED.
lv_str = <field_value>.
IF lv_counter EQ 1.
lv_filerow = lv_str.
ELSE.
CONCATENATE lv_filerow ',' lv_str INTO lv_filerow.
ENDIF.
UNASSIGN <field_value>.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDLOOP.
CLOSE DATASET iv_filename.
ev_message = 'Success'.
ELSE.
ev_message = 'Failure'.
ENDIF.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: lt_data TYPE STANDARD TABLE OF mara.
DATA: lv_filename TYPE string.
DATA: lv_message TYPE string.
SELECT * FROM mara INTO TABLE lt_data UP TO 5 ROWS.
cl_appserver_writer=>write(
EXPORTING
iv_filename = 'D:\usr\sap\testdata.csv'
it_data = lt_data
write_header = abap_true
IMPORTING
ev_message = lv_message
).
WRITE: / lv_message.
https://saponlineguides.blogspot.in/2017/09/dynamic-programming-in-abap-part-1-introduction-to-field-symbols.html
https://saponlineguides.blogspot.in/2017/09/dynamic-programming-in-abap-part-2-introduction-to-data-reference.html
ABAP Runtime Type Services (RTTS) consists of two components:
◉ Runtime Type Identification (RTTI) – Provides the methods to get the type definition of data objects at runtime.
◉ Runtime Type Creation (RTTC) – Provides the methods to create the data objects at runtime with any type definition.
Basically, ABAP RTTS provides a set of classes, whose methods can be used for runtime type identification and runtime type creation. To know more about ABAP RTTS you can follow below link:
https://wiki.scn.sap.com/wiki/pages/viewpage.action?pageId=42965
An example of dynamic programming:
Requirement: As an ABAP developer, very often we get the situation where we need to write data from an internal table to a file on application server.
Solution: We will build one class having a method which will take any internal table as input and write its content in a file on application server.
Class Definition:
CLASS cl_appserver_writer DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: write IMPORTING
iv_filename TYPE string
it_data TYPE ANY TABLE
write_header TYPE abap_bool DEFAULT space
EXPORTING
ev_message TYPE string.
ENDCLASS.
Here importing parameter it_data is of TYPE ANY TABLE so that it can receive any internal table.
Class Implementation:
CLASS cl_appserver_writer IMPLEMENTATION.
METHOD write.
TYPES: BEGIN OF ty_comp_detail,
name TYPE abap_compname,
descr TYPE scrtext_m,
END OF ty_comp_detail.
DATA: lo_type_def TYPE REF TO cl_abap_typedescr.
DATA: lo_struct_def TYPE REF TO cl_abap_structdescr.
DATA: lo_table_def TYPE REF TO cl_abap_tabledescr.
DATA: lo_data_def TYPE REF TO cl_abap_datadescr.
DATA: lo_element_def TYPE REF TO cl_abap_elemdescr.
DATA: lt_components TYPE abap_compdescr_tab.
DATA: wa_components LIKE LINE OF lt_components.
DATA: lv_str TYPE string.
DATA: lv_filerow TYPE string.
DATA: lv_counter TYPE i VALUE 0.
DATA: lw_field_info TYPE dfies.
DATA: ls_comp_detail TYPE ty_comp_detail.
DATA: lt_comp_detail TYPE TABLE OF ty_comp_detail.
FIELD-SYMBOLS: <row> TYPE any.
FIELD-SYMBOLS: <field_value> TYPE any.
* Using RTTS to get the runtime type information of the internal table
lo_type_def = cl_abap_tabledescr=>describe_by_data( it_data ).
lo_table_def ?= lo_type_def.
lo_data_def = lo_table_def->get_table_line_type( ).
lo_struct_def ?= lo_data_def.
* Get the components of the structure
lt_components = lo_struct_def->components.
CLEAR: lo_data_def.
* If the WRITE_HEADER is ABAP_TRUE then fetch the label
* of data element associated to each component of the
* line type structure of internal table, if no data element
* is associated then use component name as the header text
IF write_header EQ abap_true.
LOOP AT lt_components INTO wa_components.
lo_data_def = lo_struct_def->get_component_type( wa_components-name ).
lo_element_def ?= lo_data_def.
lw_field_info = lo_element_def->get_ddic_field( ).
ls_comp_detail-name = lw_field_info-rollname. "Get the data element name
* Calling FM to get data element text
CALL FUNCTION 'WCGW_DATA_ELEMENT_TEXT_GET'
EXPORTING
i_data_element = lw_field_info-rollname
i_language = sy-langu
IMPORTING
e_scrtext_m = ls_comp_detail-descr
EXCEPTIONS
error = 1.
IF ls_comp_detail-descr IS INITIAL.
ls_comp_detail-descr = wa_components-name.
ENDIF.
APPEND ls_comp_detail TO lt_comp_detail.
CLEAR: ls_comp_detail.
ENDLOOP.
ENDIF.
OPEN DATASET iv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
IF sy-subrc EQ 0.
* Writing header text for each column separated by comma
IF write_header EQ abap_true.
LOOP AT lt_comp_detail INTO ls_comp_detail.
lv_counter = lv_counter + 1.
IF lv_counter EQ 1.
lv_filerow = ls_comp_detail-descr.
ELSE.
CONCATENATE lv_filerow ',' ls_comp_detail-descr INTO lv_filerow.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDIF.
* Writing internal table content separated by comma
LOOP AT it_data ASSIGNING <row>.
LOOP AT lt_components INTO wa_components.
lv_counter = lv_counter + 1.
ASSIGN COMPONENT wa_components-name OF STRUCTURE <row> TO <field_value>.
IF <field_value> IS ASSIGNED.
lv_str = <field_value>.
IF lv_counter EQ 1.
lv_filerow = lv_str.
ELSE.
CONCATENATE lv_filerow ',' lv_str INTO lv_filerow.
ENDIF.
UNASSIGN <field_value>.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDLOOP.
CLOSE DATASET iv_filename.
ev_message = 'Success'.
ELSE.
ev_message = 'Failure'.
ENDIF.
ENDMETHOD.
ENDCLASS.
Here the classes CL_ABAP_*DESCR are provided by the ABAP RTTS and used to get the type definition of data objects at runtime. Also we have extracted the data element name of each component of line type structure of internal table it_data using RTTS classes. Then we fetched the data element label using the FM WCGW_DATA_ELEMENT_TEXT_GET. This label is used to write the header for each column of internal table it_data if WRITE_HEADER parameter of class is provided with ABAP_TRUE.
Using the Class – The above designed class can be used as:
DATA: lt_data TYPE STANDARD TABLE OF mara.
DATA: lv_filename TYPE string.
DATA: lv_message TYPE string.
SELECT * FROM mara INTO TABLE lt_data UP TO 5 ROWS.
cl_appserver_writer=>write(
EXPORTING
iv_filename = 'D:\usr\sap\testdata.csv'
it_data = lt_data
write_header = abap_true
IMPORTING
ev_message = lv_message
).
WRITE: / lv_message.
Here we are passing one internal table of structure MARA to the class, and subsequently its content will be written on application server as comma separated values. However, we can pass internal table of any structure. This file can also be downloaded from application server to an excel spreadsheet.
So this is how field symbol, data reference, generic data type, RTTS helps in dynamic programming approach.
The complete code:
App Server Writer.txt
REPORT zwrite_appserver.
CLASS cl_appserver_writer DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: write IMPORTING
iv_filename TYPE string
it_data TYPE ANY TABLE
write_header TYPE abap_bool DEFAULT space
EXPORTING
ev_message TYPE string.
ENDCLASS.
CLASS cl_appserver_writer IMPLEMENTATION.
METHOD write.
TYPES: BEGIN OF ty_comp_detail,
name TYPE abap_compname,
descr TYPE scrtext_m,
END OF ty_comp_detail.
DATA: lo_type_def TYPE REF TO cl_abap_typedescr.
DATA: lo_struct_def TYPE REF TO cl_abap_structdescr.
DATA: lo_table_def TYPE REF TO cl_abap_tabledescr.
DATA: lo_data_def TYPE REF TO cl_abap_datadescr.
DATA: lo_element_def TYPE REF TO cl_abap_elemdescr.
DATA: lt_components TYPE abap_compdescr_tab.
DATA: wa_components LIKE LINE OF lt_components.
DATA: lv_str TYPE string.
DATA: lv_filerow TYPE string.
DATA: lv_counter TYPE i VALUE 0.
DATA: lw_field_info TYPE dfies.
DATA: ls_comp_detail TYPE ty_comp_detail.
DATA: lt_comp_detail TYPE TABLE OF ty_comp_detail.
FIELD-SYMBOLS: <row> TYPE any.
FIELD-SYMBOLS: <field_value> TYPE any.
* Using RTTS to get the runtime type information of the internal table
lo_type_def = cl_abap_tabledescr=>describe_by_data( it_data ).
lo_table_def ?= lo_type_def.
lo_data_def = lo_table_def->get_table_line_type( ).
lo_struct_def ?= lo_data_def.
lt_components = lo_struct_def->components.
CLEAR: lo_data_def.
* If the WRITE_HEADER is ABAP_TRUE then fetch the label
* of data element associated to each component of the
* line type structure of internal table, if no data element
* is associated then use component name as the header text
IF write_header EQ abap_true.
LOOP AT lt_components INTO wa_components.
lo_data_def = lo_struct_def->get_component_type( wa_components-name ).
lo_element_def ?= lo_data_def.
lw_field_info = lo_element_def->get_ddic_field( ).
ls_comp_detail-name = lw_field_info-rollname.
* Calling FM to get data element text
CALL FUNCTION 'WCGW_DATA_ELEMENT_TEXT_GET'
EXPORTING
i_data_element = lw_field_info-rollname
i_language = sy-langu
IMPORTING
e_scrtext_m = ls_comp_detail-descr
EXCEPTIONS
error = 1.
IF ls_comp_detail-descr IS INITIAL.
ls_comp_detail-descr = wa_components-name.
ENDIF.
APPEND ls_comp_detail TO lt_comp_detail.
CLEAR: ls_comp_detail.
ENDLOOP.
ENDIF.
OPEN DATASET iv_filename FOR OUTPUT IN TEXT MODE ENCODING DEFAULT.
IF sy-subrc EQ 0.
* Writing header text for each column separated by comma
IF write_header EQ abap_true.
LOOP AT lt_comp_detail INTO ls_comp_detail.
lv_counter = lv_counter + 1.
IF lv_counter EQ 1.
lv_filerow = ls_comp_detail-descr.
ELSE.
CONCATENATE lv_filerow ',' ls_comp_detail-descr INTO lv_filerow.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDIF.
* Writing internal table content separated by comma
LOOP AT it_data ASSIGNING <row>.
LOOP AT lt_components INTO wa_components.
lv_counter = lv_counter + 1.
ASSIGN COMPONENT wa_components-name OF STRUCTURE <row> TO <field_value>.
IF <field_value> IS ASSIGNED.
lv_str = <field_value>.
IF lv_counter EQ 1.
lv_filerow = lv_str.
ELSE.
CONCATENATE lv_filerow ',' lv_str INTO lv_filerow.
ENDIF.
UNASSIGN <field_value>.
ENDIF.
ENDLOOP.
TRANSFER lv_filerow TO iv_filename.
CLEAR: lv_filerow, lv_counter.
ENDLOOP.
CLOSE DATASET iv_filename.
ev_message = 'Success'.
ELSE.
ev_message = 'Failure'.
ENDIF.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: lt_data TYPE STANDARD TABLE OF mara.
DATA: lv_filename TYPE string.
DATA: lv_message TYPE string.
SELECT * FROM mara INTO TABLE lt_data UP TO 5 ROWS.
cl_appserver_writer=>write(
EXPORTING
iv_filename = 'D:\usr\sap\testdata.csv'
it_data = lt_data
write_header = abap_true
IMPORTING
ev_message = lv_message
).
WRITE: / lv_message.
Thanks for sharing useful information..
ReplyDeleteSAP Grc training
SAP Hana training
SAP mm training
SAP pm training
SAP PP training
SAP Qm training
SAP Sd training
SAP Security training
This post is extremely radiant. I extremely like this post. It is outstanding amongst other posts that I’ve read in quite a while. Much obliged for this better than average post. I truly value it! sap fico course fees in hyderabad
ReplyDeleteJust a request, whenever you write a blog with a code snippet, please show the output as well. Many readers (like me) may not have an SAP system standby to execute the code and see the output.
ReplyDelete