[ABAP] ABAP2XLSX: Excel-Daten (*.xlsx) einlesen und anzeigen

**********************************************************************
* Types
**********************************************************************
TYPES: BEGIN OF ty_xl_line,
         col1 TYPE string,
         col2 TYPE string,
       END OF ty_xl_line.

TYPES: ty_it_xl_lines TYPE STANDARD TABLE OF ty_xl_line WITH DEFAULT KEY.

**********************************************************************
* Selektionsbild
**********************************************************************
PARAMETERS: p_fname TYPE file_table-filename OBLIGATORY.

* wenn die F4-Hilfe für den Dateinamen aufgerufen wird
AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_fname.

  DATA: lv_rc TYPE i.
  DATA: it_files TYPE filetable.
  DATA: lv_action TYPE i.

* File-Tabelle leeren, da hier noch alte Einträge von vorherigen Aufrufen drin stehen können
  CLEAR it_files.

* FileOpen-Dialog aufrufen
  TRY.
      cl_gui_frontend_services=>file_open_dialog( EXPORTING
                                                    file_filter    = |xlsx (*.xlsx)\|*.xlsx\|{ cl_gui_frontend_services=>filetype_all }|
                                                    multiselection = abap_false
                                                  CHANGING
                                                    file_table  = it_files
                                                    rc          = lv_rc
                                                    user_action = lv_action ).

      IF lv_action = cl_gui_frontend_services=>action_ok.
* wenn Datei ausgewählt wurde
        IF lines( it_files ) > 0.
* ersten Tabelleneintrag lesen
          p_fname = it_files[ 1 ]-filename.
        ENDIF.
      ENDIF.

    CATCH cx_root INTO DATA(e_text).
      MESSAGE e_text->get_text( ) TYPE 'I'.
  ENDTRY.

START-OF-SELECTION.

  DATA(it_xl) = VALUE ty_it_xl_lines( ).

  TRY.
* Reader-Objekt erzeugen
      DATA(o_reader) = CAST zif_excel_reader( NEW zcl_excel_reader_2007( ) ).
      DATA(o_excel) = o_reader->load_file( p_fname ).

* Worksheet
      DATA(o_worksheet) = o_excel->get_active_worksheet( ).
* max. Zeile und Spalte holen
      DATA(lv_max_col) = o_worksheet->get_highest_column( ).
      DATA(lv_max_row)    = o_worksheet->get_highest_row( ).

      WRITE: / lv_max_col, ',', lv_max_row.

      DATA(lv_row) = 1.

* Worksheet zeilenweise durchlaufen
      WHILE lv_row <= lv_max_row.
* Spaltennummer (1) in Excel-Spalten-Bezeichner (A) umwandeln
        DATA(lv_col_str) = zcl_excel_common=>convert_column2alpha( 1 ).

* Zellinhalt Spalte 1 holen
        o_worksheet->get_cell( EXPORTING
                                 ip_column = lv_col_str
                                 ip_row    = lv_row
                               IMPORTING
                                 ep_value  = DATA(lv_value)
                             ).

* Spaltennummer (2) in Excel-Spalten-Bezeichner (B) umwandeln
        DATA(lv_col_str2) = zcl_excel_common=>convert_column2alpha( 2 ).
* Zellinhalt Spalte 2 holen
        o_worksheet->get_cell( EXPORTING
                                 ip_column = lv_col_str2
                                 ip_row    = lv_row
                               IMPORTING
                                 ep_value  = DATA(lv_value2)
                             ).

* Werte an interne Tabelle anfügen
        APPEND VALUE #(
                        col1 = lv_value
                        col2 = lv_value2
                      ) TO it_xl.

        lv_row = lv_row + 1.
      ENDWHILE.

* Daten in SALV-Grid anzeigen
      TRY.
* SALV-Table
          DATA: o_salv TYPE REF TO cl_salv_table.

          cl_salv_table=>factory( IMPORTING
                                    r_salv_table = o_salv
                                  CHANGING
                                    t_table      = it_xl ).

* Grundeinstellungen
          o_salv->get_functions( )->set_all( abap_true ).
          o_salv->get_columns( )->set_optimize( abap_true ).
          o_salv->get_display_settings( )->set_list_header( |Excel-Werte ({ lines( it_xl ) })| ).
          o_salv->get_display_settings( )->set_striped_pattern( abap_true ).
          o_salv->get_selections( )->set_selection_mode( if_salv_c_selection_mode=>row_column ).

* Spaltenüberschriften: technischer Name und Beschreibungstexte, Short Text und Medium Text leer lassen für Autosize
          LOOP AT o_salv->get_columns( )->get( ) ASSIGNING FIELD-SYMBOL(<c>).
            DATA(o_col) = <c>-r_column.
            o_col->set_short_text( || ).
            o_col->set_medium_text( || ).
            o_col->set_long_text( |{ o_col->get_columnname( ) } [{ o_col->get_long_text( ) }]| ).
          ENDLOOP.

          o_salv->display( ).
        CATCH cx_root INTO DATA(e_txt).
          WRITE: / e_txt->get_text( ).
      ENDTRY.
    CATCH cx_root INTO DATA(ex).    " Exceptions for ABAP2XLSX
      WRITE: / ex->get_text( ).
      WRITE: / ex->get_longtext( ).
  ENDTRY.

[ABAP] ABAP2XLSX: Interne Tabelle als Excel-Datei (*.xlsx) aufbereiten und mit Desktop Office Integration (DOI) als Excel inplace anzeigen

Variante 1 (Daten aus der Tabelle SFLIGHT anzeigen)

* Dummy-Screen für cl_gui_container=>default_screen deklarieren
SELECTION-SCREEN BEGIN OF SCREEN 100.
SELECTION-SCREEN END OF SCREEN 100.

START-OF-SELECTION.

* Daten holen
  SELECT * FROM sflight INTO TABLE @DATA(it_sflight).

  TRY.
* ABAP2XLSX
      DATA: o_xl TYPE REF TO zcl_excel.

* Converter itab->ABAP2XLSX
      DATA(o_converter) = NEW zcl_excel_converter( ).

      o_converter->convert( EXPORTING
                              it_table = it_sflight
                            CHANGING
                              co_excel = o_xl ).

* aktive Worksheet
      DATA(o_xl_ws) = o_xl->get_active_worksheet( ).
* oberste Zeile einfrieren -> beim Scrollen immer sichtbar
      o_xl_ws->freeze_panes( ip_num_rows = 1 ).

* Binärdaten (xstring) generieren
      DATA(o_xlwriter) = CAST zif_excel_writer( NEW zcl_excel_writer_2007( ) ).
      DATA(lv_xl_xdata) = o_xlwriter->write_file( o_xl ).

* Daten mit DOI (Excel inplace) anzeigen
      DATA: o_oic TYPE REF TO i_oi_container_control.
      DATA: o_oid TYPE REF TO i_oi_document_proxy.
      DATA: o_err TYPE REF TO i_oi_error.
      DATA: it_errors TYPE STANDARD TABLE OF REF TO i_oi_error WITH NON-UNIQUE DEFAULT KEY.

      c_oi_container_control_creator=>get_container_control( IMPORTING
                                                               control = o_oic
                                                               error   = o_err ).

      APPEND o_err TO it_errors.

      IF abap_true = o_err->has_succeeded.
        o_oic->init_control( EXPORTING
                               inplace_enabled     = abap_true
                               no_flush            = abap_true
                               r3_application_name = 'Flugverbindungen'
                               parent              = cl_gui_container=>default_screen
                             IMPORTING
                               error               = o_err ).

        APPEND o_err TO it_errors.

        IF abap_true = o_err->has_succeeded.
          o_oic->get_document_proxy( EXPORTING
                                       document_type  = soi_doctype_excel_sheet " 'Excel.Sheet'
                                     IMPORTING
                                       document_proxy = o_oid
                                       error          = o_err ).
          APPEND o_err TO it_errors.

          IF abap_true = o_err->has_succeeded.
            DATA(it_raw) = cl_bcs_convert=>xstring_to_solix( iv_xstring = lv_xl_xdata ).
            DATA(lv_rawlen) = xstrlen( lv_xl_xdata ).

* Binärdaten des *.xlsx im Hintergrund an DOI-Objekt senden und anzeigen
            o_oid->open_document_from_table( EXPORTING
                                               document_size  = lv_rawlen
                                               document_table = it_raw
                                               open_inplace   = abap_true
                                             IMPORTING
                                               error          = o_err ).

            APPEND o_err TO it_errors.

            IF abap_true = o_err->has_succeeded.

* leere Toolbar ausblenden
              cl_abap_list_layout=>suppress_toolbar( ).

* cl_gui_container=>default_screen erzwingen
              CALL SCREEN 100.

* Schließen eines Dokuments in der Office-Anwendung
              o_oid->close_document( IMPORTING error = o_err ).

              APPEND o_err TO it_errors.

              IF o_err->has_succeeded = abap_true.

* Schließen eines Visual-Basic-Dokuments
                o_oid->close_activex_document( IMPORTING error = o_err ).

                APPEND o_err TO it_errors.

                IF o_err->has_succeeded = abap_true.

* Freigabe des für das Dokument reservierten Speichers
                  o_oid->release_document( IMPORTING error = o_err ).

                  APPEND o_err TO it_errors.

                  IF o_err->has_succeeded = abap_true.
                    FREE: o_oid.
                  ENDIF.
                ENDIF.
              ENDIF.
            ENDIF.

* baut das Control ab
            o_oic->destroy_control( IMPORTING error = o_err ).

            APPEND o_err TO it_errors.

            IF o_err->has_succeeded = abap_true.
              FREE: o_oic.
            ENDIF.

          ENDIF.
        ENDIF.
      ENDIF.

* Ausgabe Fehlerprotokoll
      DATA: lv_message_id TYPE sy-msgid.
      DATA: lv_message_number TYPE sy-msgno.
      DATA: lv_param1 TYPE sy-msgv1.
      DATA: lv_param2 TYPE sy-msgv2.
      DATA: lv_param3 TYPE sy-msgv3.
      DATA: lv_param4 TYPE sy-msgv4.

      LOOP AT it_errors ASSIGNING FIELD-SYMBOL(<e>).
        <e>->get_message( IMPORTING
                            message_id     = lv_message_id
                            message_number = lv_message_number
                            param1         = lv_param1
                            param2         = lv_param2
                            param3         = lv_param3
                            param4         = lv_param4
                        ).
        WRITE: / <e>->error_code, lv_message_id, lv_message_number, lv_param1, lv_param2, lv_param3, lv_param4.
      ENDLOOP.

    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

Variante 2 (Daten aus der Tabelle SFLIGHT anzeigen, Styles festlegen)

* Dummy-Screen für cl_gui_container=>default_screen deklarieren
SELECTION-SCREEN BEGIN OF SCREEN 100.
SELECTION-SCREEN END OF SCREEN 100.

START-OF-SELECTION.

* Daten holen
  SELECT * FROM sflight INTO TABLE @DATA(it_sflight).

  TRY.
* ABAP2XLSX
      DATA(o_xl) = NEW zcl_excel( ).
* aktive Worksheet
      DATA(o_xl_ws) = o_xl->get_active_worksheet( ).
* Name der Sheet
      o_xl_ws->set_title( ip_title = 'Flights' ).

* Tabelle übergeben und Style festlegen
      o_xl_ws->bind_table( ip_table          = it_sflight
                           is_table_settings = VALUE #(
                                                        table_style      = zcl_excel_table=>builtinstyle_medium2
*                                                  show_row_stripes = abap_true
                                                      )
                         ).

* oberste Zeile einfrieren -> beim Scrollen immer sichtbar
      o_xl_ws->freeze_panes( ip_num_rows = 1 ).

* Binärdaten (xstring) generieren
      DATA(o_xlwriter) = CAST zif_excel_writer( NEW zcl_excel_writer_2007( ) ).
      DATA(lv_xl_xdata) = o_xlwriter->write_file( o_xl ).

* Daten mit DOI (Excel inplace) anzeigen
      DATA: o_oic TYPE REF TO i_oi_container_control.
      DATA: o_oid TYPE REF TO i_oi_document_proxy.
      DATA: o_err TYPE REF TO i_oi_error.
      DATA: it_errors TYPE STANDARD TABLE OF REF TO i_oi_error WITH NON-UNIQUE DEFAULT KEY.

      c_oi_container_control_creator=>get_container_control( IMPORTING
                                                               control = o_oic
                                                               error   = o_err ).

      APPEND o_err TO it_errors.

      IF abap_true = o_err->has_succeeded.
        o_oic->init_control( EXPORTING
                               inplace_enabled     = abap_true
                               no_flush            = abap_true
                               r3_application_name = 'Flugverbindungen'
                               parent              = cl_gui_container=>default_screen
                             IMPORTING
                               error               = o_err ).

        APPEND o_err TO it_errors.

        IF abap_true = o_err->has_succeeded.
          o_oic->get_document_proxy( EXPORTING
                                       document_type  = soi_doctype_excel_sheet " 'Excel.Sheet'
                                     IMPORTING
                                       document_proxy = o_oid
                                       error          = o_err ).

          APPEND o_err TO it_errors.

          IF abap_true = o_err->has_succeeded.
            DATA(it_raw) = cl_bcs_convert=>xstring_to_solix( iv_xstring = lv_xl_xdata ).
            DATA(lv_rawlen) = xstrlen( lv_xl_xdata ).

* Binärdaten des *.xlsx im Hintergrund an DOI-Objekt senden und anzeigen
            o_oid->open_document_from_table( EXPORTING
                                               document_size    = lv_rawlen
                                               document_table   = it_raw
                                               open_inplace     = abap_true
                                             IMPORTING
                                               error          = o_err ).

            APPEND o_err TO it_errors.

            IF abap_true = o_err->has_succeeded.

* leere Toolbar ausblenden
              cl_abap_list_layout=>suppress_toolbar( ).

* cl_gui_container=>default_screen erzwingen
              CALL SCREEN 100.

* Schließen eines Dokuments in der Office-Anwendung
              o_oid->close_document( IMPORTING error = o_err ).

              APPEND o_err TO it_errors.

              IF o_err->has_succeeded = abap_true.

* Schließen eines Visual-Basic-Dokuments
                o_oid->close_activex_document( IMPORTING error = o_err ).

                APPEND o_err TO it_errors.

                IF o_err->has_succeeded = abap_true.

* Freigabe des für das Dokument reservierten Speichers
                  o_oid->release_document( IMPORTING error = o_err ).

                  APPEND o_err TO it_errors.

                  IF o_err->has_succeeded = abap_true.
                    FREE: o_oid.
                  ENDIF.
                ENDIF.
              ENDIF.
            ENDIF.

* baut das Control ab
            o_oic->destroy_control( IMPORTING error = o_err ).

            APPEND o_err TO it_errors.

            IF o_err->has_succeeded = abap_true.
              FREE: o_oic.
            ENDIF.

          ENDIF.
        ENDIF.
      ENDIF.

* Ausgabe Fehlerprotokoll
      DATA: lv_message_id TYPE sy-msgid.
      DATA: lv_message_number TYPE sy-msgno.
      DATA: lv_param1 TYPE sy-msgv1.
      DATA: lv_param2 TYPE sy-msgv2.
      DATA: lv_param3 TYPE sy-msgv3.
      DATA: lv_param4 TYPE sy-msgv4.

      LOOP AT it_errors ASSIGNING FIELD-SYMBOL(<e>).
        <e>->get_message( IMPORTING
                            message_id     = lv_message_id
                            message_number = lv_message_number
                            param1         = lv_param1
                            param2         = lv_param2
                            param3         = lv_param3
                            param4         = lv_param4
                        ).
        WRITE: / <e>->error_code, lv_message_id, lv_message_number, lv_param1, lv_param2, lv_param3, lv_param4.
      ENDLOOP.

    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

Variante 3 (Daten aus der Tabelle SFLIGHT anzeigen, Komplexbeispiel)

* Dummy-Screen für cl_gui_container=>default_screen deklarieren
SELECTION-SCREEN BEGIN OF SCREEN 100.
SELECTION-SCREEN END OF SCREEN 100.

START-OF-SELECTION.

* Daten holen
  SELECT * FROM sflight INTO TABLE @DATA(it_sflight).

  TRY.
* ABAP2XLSX
      DATA(o_xl) = NEW zcl_excel( ).
* aktive Worksheet
      DATA(o_xl_ws) = o_xl->get_active_worksheet( ).
* Name der Sheet
      o_xl_ws->set_title( ip_title = 'Flights' ).
* Gitterlinien ausschalten
      o_xl_ws->set_show_gridlines( i_show_gridlines = abap_false ).
* oberste zwei Zeilen einfrieren -> beim Scrollen immer sichtbar
      o_xl_ws->freeze_panes( ip_num_rows = 2 ).

* RTTS: Table: Beschreibung
      DATA(o_tdesc) = CAST cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_data( it_sflight ) ).
* RTTS: Row: Beschreibung
      DATA(o_sdesc) = CAST cl_abap_structdescr( o_tdesc->get_table_line_type( ) ).
* RTTS: Col: Komponenten (Felder) -> DFIES: DD-Schnittstelle: Tabellenfelder für DDIF_FIELDINFO_GET
      DATA(it_com) = o_sdesc->get_components( ).

* Header einfügen
      LOOP AT it_com ASSIGNING FIELD-SYMBOL(<c>).
        DATA(lv_col_head) = sy-tabix.
* Prüfen, ob DDIC-Typ, damit die Metadaten ausgelesen werden können
        DATA(o_edesc_head) = CAST cl_abap_elemdescr( <c>-type ).
        IF o_edesc_head->is_ddic_type( ) EQ abap_true.
          DATA(lv_dfies_head) = o_edesc_head->get_ddic_field( ).
* technischer Name in die erste Zeile
          o_xl_ws->set_cell( ip_column = lv_col_head
                             ip_row    = 1
                             ip_value  = <c>-name ).
* Langtext des Feldes in die zweite Zeile
          o_xl_ws->set_cell( ip_column = lv_col_head
                             ip_row    = 2
                             ip_value  = lv_dfies_head-scrtext_l ).

* Spaltenbreite setzen
          o_xl_ws->get_column( lv_col_head )->set_width( strlen( lv_dfies_head-scrtext_l ) + 5 ).
        ENDIF.

      ENDLOOP.

* Daten einfügen
      LOOP AT it_sflight ASSIGNING FIELD-SYMBOL(<row>).
        DATA(lv_row) = sy-tabix + 2.

        LOOP AT it_com ASSIGNING FIELD-SYMBOL(<col>).
          DATA(lv_col) = sy-tabix.

* Prüfen, ob DDIC-Typ, damit die Metadaten ausgelesen werden können
          DATA(o_edesc) = CAST cl_abap_elemdescr( <col>-type ).
          IF o_edesc->is_ddic_type( ) EQ abap_true.
* wenn DDIC-Typ, dann Metainformationen auslesen
            DATA(lv_dfies) = o_edesc->get_ddic_field( ).
* anhand des Komponentennamens den akt. Zellinhalt einer Zeile lesen
            ASSIGN COMPONENT <col>-name OF STRUCTURE <row> TO FIELD-SYMBOL(<cell>).
            IF <cell> IS ASSIGNED.
              o_xl_ws->set_cell( ip_column = lv_col
                                 ip_row    = lv_row
                                 ip_value  = <cell> ).
            ENDIF.
          ENDIF.
        ENDLOOP.

* Zeilenhöhe
        o_xl_ws->get_row( ip_row = lv_row )->set_row_height( 15 ).
      ENDLOOP.

* Autofilter setzen
      DATA(o_autofilter) = o_xl->add_new_autofilter( io_sheet = o_xl_ws ).
      o_autofilter->set_filter_area( is_area = VALUE #( row_start = 2
                                                        col_start = 1
                                                        row_end   = o_xl_ws->get_highest_row( )
                                                        col_end   = o_xl_ws->get_highest_column( ) ) ).

* Binärdaten (xstring) generieren
      DATA(o_xlwriter) = CAST zif_excel_writer( NEW zcl_excel_writer_2007( ) ).
      DATA(lv_xl_xdata) = o_xlwriter->write_file( o_xl ).

* Daten mit DOI (Excel inplace) anzeigen
      DATA: o_oic TYPE REF TO i_oi_container_control.
      DATA: o_oid TYPE REF TO i_oi_document_proxy.
      DATA: o_err TYPE REF TO i_oi_error.
      DATA: it_errors TYPE STANDARD TABLE OF REF TO i_oi_error WITH NON-UNIQUE DEFAULT KEY.

      c_oi_container_control_creator=>get_container_control( IMPORTING
                                                               control = o_oic
                                                               error   = o_err ).

      APPEND o_err TO it_errors.

      IF abap_true = o_err->has_succeeded.
        o_oic->init_control( EXPORTING
                               inplace_enabled     = abap_true
                               no_flush            = abap_true
                               r3_application_name = 'Flugverbindungen'
                               parent              = cl_gui_container=>default_screen
                             IMPORTING
                               error               = o_err ).

        APPEND o_err TO it_errors.

        IF abap_true = o_err->has_succeeded.
          o_oic->get_document_proxy( EXPORTING
                                       document_type  = soi_doctype_excel_sheet " 'Excel.Sheet'
                                     IMPORTING
                                       document_proxy = o_oid
                                       error          = o_err ).

          APPEND o_err TO it_errors.

          IF abap_true = o_err->has_succeeded.
            DATA(it_raw) = cl_bcs_convert=>xstring_to_solix( iv_xstring = lv_xl_xdata ).
            DATA(lv_rawlen) = xstrlen( lv_xl_xdata ).

* Binärdaten des *.xlsx im Hintergrund an DOI-Objekt senden und anzeigen
            o_oid->open_document_from_table( EXPORTING
                                               document_size  = lv_rawlen
                                               document_table = it_raw
                                               open_inplace   = abap_true
                                             IMPORTING
                                               error          = o_err ).

            APPEND o_err TO it_errors.

            IF abap_true = o_err->has_succeeded.
* leere Toolbar ausblenden
              cl_abap_list_layout=>suppress_toolbar( ).

* cl_gui_container=>default_screen erzwingen
              CALL SCREEN 100.

* Schließen eines Dokuments in der Office-Anwendung
              o_oid->close_document( IMPORTING error = o_err ).

              APPEND o_err TO it_errors.

              IF o_err->has_succeeded = abap_true.

* Schließen eines Visual-Basic-Dokuments
                o_oid->close_activex_document( IMPORTING error = o_err ).

                APPEND o_err TO it_errors.

                IF o_err->has_succeeded = abap_true.

* Freigabe des für das Dokument reservierten Speichers
                  o_oid->release_document( IMPORTING error = o_err ).

                  APPEND o_err TO it_errors.

                  IF o_err->has_succeeded = abap_true.
                    FREE: o_oid.
                  ENDIF.
                ENDIF.
              ENDIF.
            ENDIF.

* baut das Control ab
            o_oic->destroy_control( IMPORTING error = o_err ).

            APPEND o_err TO it_errors.

            IF o_err->has_succeeded = abap_true.
              FREE: o_oic.
            ENDIF.

          ENDIF.
        ENDIF.
      ENDIF.

* Ausgabe Fehlerprotokoll
      DATA: lv_message_id TYPE sy-msgid.
      DATA: lv_message_number TYPE sy-msgno.
      DATA: lv_param1 TYPE sy-msgv1.
      DATA: lv_param2 TYPE sy-msgv2.
      DATA: lv_param3 TYPE sy-msgv3.
      DATA: lv_param4 TYPE sy-msgv4.

      LOOP AT it_errors ASSIGNING FIELD-SYMBOL(<e>).
        <e>->get_message( IMPORTING
                            message_id     = lv_message_id
                            message_number = lv_message_number
                            param1         = lv_param1
                            param2         = lv_param2
                            param3         = lv_param3
                            param4         = lv_param4
                        ).
        WRITE: / <e>->error_code, lv_message_id, lv_message_number, lv_param1, lv_param2, lv_param3, lv_param4.
      ENDLOOP.

    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.