Wednesday, March 21, 2018

ABAP Class to Import CSV Files

class Z_BC_USER_FILEIO definition
 public
 final
 create public .

public section.

 type-pools ABAP .
 methods UPLOAD
  importing
   !I_SHOW_DIALOG type ABAP_BOOL default ' '
   !I_WINDOW_TITLE type STRING default ''
   !I_INITIAL_DIRECTORY type STRING default ''
   !I_DEFAULT_FILE type STRING default '*.*'
   !I_DEFAULT_EXTENSION type STRING default '*.*'
   !I_FILE_FILTER type STRING default '*.*'
   !I_UPLOAD_PATH type STRING default ''
   !I_CSV_PARSE type ABAP_BOOL default ' '
   !I_CSV_DATA_SEP type CHAR1 default ','
   !I_CSV_ESC_CHAR type CHAR1 default '"'
   !I_CSV_NUM_COLS type I default 9999
   !I_CSV_SKIPHEAD type ABAP_BOOL default ' '
  exporting
   !E_FILE_RAWDATA type TEXTLINE_T
  changing
   !E_FILE_CSV_STRUCT type ANY optional
   !E_FILE_CSV_TABLE type STANDARD TABLE optional
  exceptions
   UPLOAD_ERROR
   PARSE_ERROR
   PATH_ERROR
   PROGRAM_ERROR .

private section.

 data P_UPLOAD_PATH type STRING .

 type-pools ABAP .
 class-methods PARSE_CSV
  importing
   !I_CSV_DATA_SEP type CHAR1 default ','
   !I_CSV_ESC_CHAR type CHAR1 default '"'
   !I_CSV_NUM_COLS type I default 9999
   !I_CSV_SKIPHEAD type ABAP_BOOL default ' '
   !I_FILE_RAWDATA type TEXTLINE_T
  exporting
   !E_FILE_CSV_STRUCT type ANY
   !E_FILE_CSV_TABLE type STANDARD TABLE
  exceptions
   PARSE_ERROR
   TABLE_ERROR .


METHOD upload.
 DATA: lf_error TYPE i VALUE 0,
    lf_upload_path TYPE char1024,
    lf_window_title TYPE string.

 p_upload_path = i_upload_path.
 lf_upload_path = p_upload_path.

 " let user select file if needed
 IF i_show_dialog = 'X'.
  IF I_WINDOW_TITLE IS INITIAL.
   lf_window_title = text-002.
  ELSE.
   lf_window_title = I_WINDOW_TITLE.
  ENDIF.

  CALL FUNCTION '/SAPSLL/BROWSE_LOCAL_FILE_SYST'
   EXPORTING
    iv_window_title = lf_window_title
    iv_default_extension = i_default_extension
    iv_default_filename = i_default_file
    iv_file_filter = i_file_filter
    iv_initial_directory = i_initial_directory
    iv_multiselection = ' '
   IMPORTING
    ev_rcode = lf_error
   CHANGING
    iv_path = lf_upload_path
    .
 ELSE.
  " Must have the path if not using the dialog
  IF p_upload_path IS INITIAL.
   lf_error = 1.
  ENDIF.
 ENDIF.

 " upload the file if we have a path
 IF lf_error = 0.
  CALL METHOD cl_gui_frontend_services =>gui_upload
   EXPORTING
    filename = p_upload_path
   CHANGING
    data_tab = e_file_rawdata
   EXCEPTIONS
    file_open_error = 1
    file_read_error = 2
    no_batch = 3
    gui_refuse_filetransfer = 4
    invalid_type = 5
    no_authority = 6
    unknown_error = 7
    bad_data_format = 8
    header_not_allowed = 9
    separator_not_allowed = 10
    header_too_long = 11
    unknown_dp_error = 12
    access_denied = 13
    dp_out_of_memory = 14
    disk_full = 15
    dp_timeout = 16
    not_supported_by_gui = 17
    error_no_gui = 18
    others = 19
      .
  IF sy-subrc = 0.
   IF i_csv_parse = 'X'.
    "IF ( e_file_csv_struct IS NOT INITIAL ) AND ( e_file_csv_table IS NOT INITIAL ).
    CALL METHOD parse_csv
     EXPORTING
      i_csv_data_sep = i_csv_data_sep
      i_csv_esc_char = i_csv_esc_char
      i_file_rawdata = e_file_rawdata
      i_csv_num_cols = i_csv_num_cols
      i_csv_skiphead = i_csv_skiphead
     IMPORTING
      e_file_csv_struct = e_file_csv_struct
      e_file_csv_table = e_file_csv_table
     EXCEPTIONS
      parse_error = 1
      table_error = 2
      OTHERS = 3
      .
    IF sy-subrc <> 0.
     MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
           WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
           RAISING parse_error.
    ENDIF.

    "ELSE. " I_CSV_PARSE set without other CSV parameters
    " MESSAGE s000(Z_gts) DISPLAY LIKE 'E'
    "     WITH text-001 space space space
    "     RAISING program_error.
    "ENDIF.
   ENDIF.

  ELSE. " GUI_UPLOAD error
   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
         WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
         RAISING upload_error.
  ENDIF.

 ELSE. " Path not set or error with user dialog
  MESSAGE s000(Z_gts) DISPLAY LIKE 'E'
      WITH text-000 p_upload_path space space
      RAISING path_error.
 ENDIF.
ENDMETHOD.


METHOD parse_csv.
 DATA: lt_csv_table_struct TYPE REF TO data,
    lt_csv_table_desc TYPE REF TO cl_abap_tabledescr,
    ls_csv_table_desc TYPE REF TO cl_abap_structdescr,
    lt_csv_table_flds TYPE abap_compdescr_tab,
    ls_csv_table_flds TYPE abap_compdescr,
    ls_file_rawline TYPE LINE OF textline_t,
    lt_file_columns TYPE table_of_strings,
    ls_file_columns TYPE LINE OF table_of_strings,
    lf_skip_line  TYPE abap_bool VALUE ' ',
    ls_fieldcatalog TYPE lvc_s_fcat,
    lt_fieldcatalog TYPE lvc_t_fcat,
    lt_dynamictable TYPE REF TO data,
    ls_dynamicline TYPE REF TO data,
    lf_loop_count  TYPE i,
    lf_line_count  TYPE i,
    lf_fieldname  TYPE char30.

 FIELD-SYMBOLS: <fs_csv_table_struct> TYPE STANDARD TABLE,
         <fs_dynamic_table> TYPE STANDARD TABLE,
         <fs_dynamic_lines> TYPE ANY,
         <fs_dynamic_field> TYPE ANY.

 " determine the structure of the destination table
 GET REFERENCE OF e_file_csv_table INTO lt_csv_table_struct.
 ASSIGN lt_csv_table_struct->* TO <fs_csv_table_struct>.
 lt_csv_table_desc ? =
  cl_abap_structdescr =>describe_by_data_ref( lt_csv_table_struct ).
 ls_csv_table_desc ? = lt_csv_table_desc->get_table_line_type( ).
 lt_csv_table_flds = ls_csv_table_desc->components.

 " use destination table structure for results table
 LOOP AT lt_csv_table_flds INTO ls_csv_table_flds.
  ls_fieldcatalog-fieldname = ls_csv_table_flds-name.
  ls_fieldcatalog-inttype = ls_csv_table_flds-type_kind.
  ls_fieldcatalog-intlen = ls_csv_table_flds-length.
  ls_fieldcatalog-decimals = ls_csv_table_flds-decimals.
  APPEND ls_fieldcatalog TO lt_fieldcatalog.
 ENDLOOP.

 " build a dynamic table to put the CSV contents into
 CALL METHOD cl_alv_table_create =>create_dynamic_table
  EXPORTING
   it_fieldcatalog = lt_fieldcatalog
  IMPORTING
   ep_table = lt_dynamictable
  EXCEPTIONS
   generate_subpool_dir_full = 1
   OTHERS = 2.
 IF sy-subrc <> 0.
  MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
        WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
        RAISING table_error.
 ENDIF.

 " finish dynamic table, create dynamic table lines
 ASSIGN lt_dynamictable->* TO <fs_dynamic_table>.
 CREATE DATA ls_dynamicline LIKE LINE OF <fs_dynamic_table>.
 ASSIGN ls_dynamicline->* TO <fs_dynamic_lines>.

 " delete header line if needed
 IF i_csv_skiphead = 'X'.
  lf_skip_line = 'X'.
 ENDIF.

 " process the CSV file raw data
 LOOP AT i_file_rawdata INTO ls_file_rawline.
  CALL FUNCTION 'RSDS_CONVERT_CSV'
   EXPORTING
    i_data_sep = i_csv_data_sep
    i_esc_char = i_csv_esc_char
    i_record = ls_file_rawline
    i_field_count = i_csv_num_cols
   IMPORTING
    e_t_data = lt_file_columns
   EXCEPTIONS
    escape_no_close = 1
    escape_improper = 2
    conversion_error = 3
    OTHERS = 4.
  IF sy-subrc <> 0. " RSDS_CONVERT_CSV error
   MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
         WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
         RAISING parse_error.
  ENDIF.

  " store CSV fields into dynamic table
  lf_loop_count = 1.
  DESCRIBE TABLE lt_file_columns LINES lf_line_count.
  LOOP AT lt_csv_table_flds INTO ls_csv_table_flds.
   MOVE ls_csv_table_flds-name TO lf_fieldname.
   ASSIGN COMPONENT lf_fieldname
    OF STRUCTURE <fs_dynamic_lines>
TO <fs_dynamic_field>.
   READ TABLE lt_file_columns INDEX lf_loop_count INTO ls_file_columns.

   IF lf_loop_count < = lf_line_count.
    <fs_dynamic_field> = ls_file_columns.
   ELSE.
    <fs_dynamic_field> = ''.
   ENDIF.

   lf_loop_count = lf_loop_count + 1.
  ENDLOOP.

  " move dynamic table line to resultant table, unless we are skipping
  IF lf_skip_line = ' '.
   MOVE-CORRESPONDING <fs_dynamic_lines> TO e_file_csv_struct.
   APPEND e_file_csv_struct TO e_file_csv_table.
  ELSE.
   lf_skip_line = ' '.
  ENDIF.
 ENDLOOP.
ENDMETHOD.

No comments:

Post a Comment