Skip to Content
Technical Articles
Author's profile photo Juwin Pallipat Thomas

SALV Tree to Excel (xlsx)

This document is to explain how to convert the SALV Tree, created using CL_SALV_TREE class, to an Excel file. There is a standard method to convert the SALV Table, created using CL_SALV_TABLE, into an Excel file. But, I couldn’t find a similar method isn’t available for SALV Tree. There are other solutions available in the market. But, this solution, I felt, has the lowest amount of custom code (~100 lines).

Approach

If you look closely at the SALV Tree output, you may see that, the Tree is essentially a Table itself.

2015-07-24 15_25_46-SAP.jpg

2015-07-24 15_26_26-Tree.jpg

To make a Table to a Tree, the only additions required are Groupings and Indentations. This is the concept I have used here, to create the Excel file – create a table and then add groupings and indentations to it.

Solution

The input for this development is a CL_SALV_TREE object. The code below creates an simple object with name LR_TREE, using common tables. This portion of the code, isn’t part of the solution – it is just used to get ‘test data’ to test the solution.

data:     begin          of   ls_mara,
          matnr          type matnr,
          maktx          type matnr,
          end            of   ls_mara,
          lt_mara        like standard table of ls_mara,
          begin          of   ls_marc,
          matnr          type matnr,
          werks          type marc-werks,
          name1          type t001w-name1,
          end            of   ls_marc,
          lt_marc        like standard table of ls_marc,
          begin          of   ls_mard,
          matnr          type matnr,
          werks          type marc-werks,
          lgort          type mard-lgort,
          lgobe          type t001l-lgobe,
          end            of   ls_mard,
          lt_mard        like standard table of ls_mard,
          begin          of   ls_mchb,
          matnr          type matnr,
          werks          type marc-werks,
          lgort          type mard-lgort,
          charg          type mchb-charg,
          clabs          type labst,
          cumlm          type umlmd,
          cinsm          type insme,
          ceinm          type einme,
          cspem          type speme,
          cretm          type retme,
          end            of   ls_mchb,
          lt_mchb        like standard table of ls_mchb,
          begin          of   ls_output,
          area           type string,
          clabs          type labst,
          cumlm          type umlmd,
          cinsm          type insme,
          ceinm          type einme,
          cspem          type speme,
          cretm          type retme,
          end            of   ls_output,
          lt_output      like standard table of ls_output,
          ls_marakey     type lvc_nkey,
          ls_marckey     type lvc_nkey,
          ls_mardkey     type lvc_nkey,
          lr_table       type ref to cl_salv_table,
          lr_columns     type ref to cl_salv_columns_tree,
          lr_tree        type ref to cl_salv_tree,
          lr_node        type ref to cl_salv_node,
          lt_nodes       type salv_t_nodes,
          ls_node        like line of lt_nodes.

select matnr maktx
       up to 50 rows
       from makt
       into table lt_mara
       where spras = sy-langu.

select matnr a~werks name1
       from marc as a join t001w as b
         on a~werks = b~werks
       into table lt_marc
       for all entries in lt_mara
       where matnr = lt_mara-matnr.

select matnr a~werks a~lgort lgobe
       from mard as a join t001l as b
         on a~lgort = b~lgort
        and a~werks = b~werks
       into table lt_mard
       for all entries in lt_marc
       where matnr = lt_marc-matnr
         and a~werks = lt_marc-werks.

select matnr werks lgort charg clabs cumlm cinsm ceinm cspem cretm
       from mchb
       into table lt_mchb
       for all entries in lt_mard
       where matnr = lt_mard-matnr
         and werks = lt_mard-werks
         and lgort = lt_mard-lgort.

cl_salv_tree=>factory(
  importing
    r_salv_tree = lr_tree
  changing
    t_table      = lt_output ).

lr_columns = lr_tree->get_columns( ).
lr_columns->set_optimize( abap_true ).

loop at lt_mara into ls_mara.
  clear ls_output.
  concatenate ls_mara-matnr ls_mara-maktx into ls_output-area separated by space.
  loop at lt_mchb into ls_mchb where matnr = ls_mara-matnr.
    add-corresponding ls_mchb to ls_output.
  endloop.
  lr_node = lr_tree->get_nodes( )->add_node( related_node = space
                                data_row     = ls_output
                                relationship = cl_gui_column_tree=>relat_last_child ).
  ls_marakey = lr_node->get_key( ).
  loop at lt_marc into ls_marc where matnr = ls_mara-matnr.
    clear ls_output.
    concatenate ls_marc-werks ls_marc-name1 into ls_output-area separated by space.
    loop at lt_mchb into ls_mchb where matnr = ls_mara-matnr and werks = ls_marc-werks.
      add-corresponding ls_mchb to ls_output.
    endloop.
    lr_node = lr_tree->get_nodes( )->add_node( related_node = ls_marakey
                                  data_row     = ls_output
                                  relationship = cl_gui_column_tree=>relat_last_child ).
    ls_marckey = lr_node->get_key( ).
    loop at lt_mard into ls_mard where matnr = ls_mara-matnr and werks = ls_marc-werks.
      clear ls_output.
      concatenate ls_mard-lgort ls_mard-lgobe into ls_output-area separated by space.
      loop at lt_mchb into ls_mchb where matnr = ls_mara-matnr and werks = ls_marc-werks and lgort = ls_mard-lgort.
        add-corresponding ls_mchb to ls_output.
      endloop.
      lr_node = lr_tree->get_nodes( )->add_node( related_node = ls_marckey
                                    data_row     = ls_output
                                    relationship = cl_gui_column_tree=>relat_last_child ).
      ls_mardkey = lr_node->get_key( ).
      loop at lt_mchb into ls_mchb where matnr = ls_mara-matnr and werks = ls_marc-werks and lgort = ls_mard-lgort.
        clear ls_output.
        ls_output-area = ls_mchb-charg.
        move-corresponding ls_mchb to ls_output.
        lr_node = lr_tree->get_nodes( )->add_node( related_node = ls_mardkey
                                      data_row     = ls_output
                                      relationship = cl_gui_column_tree=>relat_last_child ).
      endloop.
    endloop.
  endloop.
endloop.

lr_tree->display( ).
That builds our ‘test data’ tree.
2015-07-24 15_25_46-SAP.jpg

Ideally, you should be creating the routine to convert SALV Tree to Excel file, as a global method/ function module, so that many programs may reuse it. Which means, that common routine will have to work with the SALV Tree object and extract the data for output, titles, headers etc from it. So, I am regenerating the output table here, using SALV Tree object.

The code starts here….

constants:lc_xlspace     type c value ' '. "Hexa value for this field should be 0030
data:     lv_level       type i,
          lv_xlsx        type xstring,
          lt_table       type ref to data,
          lr_data        type ref to data.
field-symbols: <data>  type any,
               <table> type standard table,
               <str>   type any.
lt_nodes = lr_tree->get_nodes( )->get_all_nodes( ).
loop at lt_nodes into ls_node.
  lr_node = ls_node-node.
  clear lv_level.
  do.
    try.
        lr_node = lr_node->get_parent( ).
        add 1 to lv_level.
      catch cx_salv_msg.
        exit.
    endtry.
  enddo.
  lr_data = ls_node-node->get_data_row( ).
  assign lr_data->* to <data>.
  if <table> is not assigned.
    create data lt_table like standard table of <data>.
    assign lt_table->* to <table>.
  endif.
  assign component 1 of structure <data> to <str>.
  subtract 1 from lv_level.
  do lv_level times.
    concatenate lc_xlspace <str> into <str>.
  enddo.
  append <data> to <table>.
endloop.

cl_salv_table=>factory(
  importing
    r_salv_table = lr_table
  changing
    t_table = <table> ).
lr_table->display( ).

2015-07-24 15_24_55-Tree.jpg

2015-07-24 15_28_58-Microsoft Excel - BatchQty.jpg

At this point, the table has indentations, but no groupings. So, the next step is to add that to the table. Groupings cannot be directly added to the internal table, it has to be done in the XML file directly. Excel needs 3 identifiers to understand the row groupings.

  1. The row element in the file, should have an attribute outlineLevel and it should have the value of the level.
  2. The sheetFormatPr element in the file, should have an attribute outlineLevelRow and it should have the number of levels used in the file.
  3. The sheetPr element in the file, should have a child node named outlinePr, with an attribute summaryBelow, with value as false. The Excel file created by SAP, doesn’t have sheetPr element, so, with this routine, we will add the new element in the file.

 

lv_xlsx = lr_table->to_xml( if_salv_bs_xml=>c_type_xlsx ).
data:     lr_zip         type ref to cl_abap_zip,
          lr_xlnode      type ref to if_ixml_node,
          lr_xldimension type ref to if_ixml_node,
          lr_xlsheetpr   type ref to if_ixml_element,
          lr_xloutlinepr type ref to if_ixml_element,
          lv_file        type xstring,
          lr_file        type ref to cl_xml_document,
          lr_xlrows      type ref to if_ixml_node_list,
          lr_xlrow       type ref to if_ixml_element,
          lr_xlformat    type ref to if_ixml_element,
          lr_xlworksheet type ref to if_ixml_element,
          lv_tabix       type i,
          lv_maxlevel    type i,
          lv_levels      type string.

create object lr_zip.
lr_zip->load( lv_xlsx ).
*Get Worksheet XML file
lr_zip->get( exporting name = 'xl/worksheets/sheet1.xml'
             importing content = lv_file ).

create object lr_file.
lr_file->parse_xstring( lv_file ).

*Row elements are under SheetData
lr_xlnode = lr_file->find_node( 'sheetData' ).
lr_xlrows = lr_xlnode->get_children( ).

do lr_xlrows->get_length( ) times.
  lv_tabix = sy-index - 1.
  lr_xlrow ?= lr_xlrows->get_item( lv_tabix ).
*Find the same node in the SALV Tree object
  read table lt_nodes into ls_node index lv_tabix.
  if sy-subrc eq 0.
    lr_node = ls_node-node.
*Find the level of the node
    clear lv_level.
    do.
      try.
          lr_node = lr_node->get_parent( ).
          add 1 to lv_level.
        catch cx_salv_msg.
          exit.
      endtry.
    enddo.
    subtract 1 from lv_level.
    if lv_level ne 0.
      lv_levels = lv_level.
      if lv_level > lv_maxlevel.
        lv_maxlevel = lv_level.
      endif.
      condense lv_levels.
*Assign the level to row
      lr_xlrow->set_attribute( name = 'outlineLevel' value = lv_levels ).
      lr_xlrow->set_attribute( name = 'hidden' value = 'true' ).
    endif.
  endif.
enddo.

*Set maximum levels used in the sheet
lv_levels = lv_maxlevel.
condense lv_levels.
lr_xlformat ?= lr_file->find_node( 'sheetFormatPr' ).
lr_xlformat->set_attribute( name = 'outlineLevelRow' value = lv_levels ).

*Create new element in the XML file
lr_xlworksheet ?= lr_file->find_node( 'worksheet' ).
lr_xldimension ?= lr_file->find_node( 'dimension' ).
lr_xlsheetpr = cl_ixml=>create( )->create_document( )->create_element( name = 'sheetPr' ).
lr_xloutlinepr = cl_ixml=>create( )->create_document( )->create_element( name = 'outlinePr' ).
lr_xlsheetpr->if_ixml_node~append_child( lr_xloutlinepr ).
lr_xloutlinepr->set_attribute( name = 'summaryBelow' value = 'false' ).
lr_xlworksheet->if_ixml_node~insert_child( new_child = lr_xlsheetpr ref_child = lr_xldimension ).

*Create Xstring file for the XML, and add it to Excel Zip file
lr_file->render_2_xstring( importing stream = lv_file ).
lr_zip->delete( exporting name = 'xl/worksheets/sheet1.xml' ).
lr_zip->add( exporting name = 'xl/worksheets/sheet1.xml'
content = lv_file ).

lv_xlsx = lr_zip->save( ).

LV_XLSX is the output file. You may download it to your desktop and see how it looks like. Now, with these changes the file Excel file shows groupings and hierarchy.

2015-07-24 15_27_22-Microsoft Excel - BatchQty.jpg

Assigned Tags

      14 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Thanks for nice example! 🙂

      Author's profile photo Ricky Orea
      Ricky Orea

      Hello, thanks for this example, very useful.

      Just wanted to ask why when you turn on aggregation for the tree and download to excel, it doesn't have the totals included.

      Is there a way to fix this?

      Thanks!

      Author's profile photo Juwin Pallipat Thomas
      Juwin Pallipat Thomas
      Blog Post Author

      Ok, I will check on it. May be the aggregation information is not getting passed onto the ALV display method.

      Thanks,

      Juwin

      Author's profile photo Juwin Pallipat Thomas
      Juwin Pallipat Thomas
      Blog Post Author

      The aggregation cannot be passed on to ALV method, because it interprets and creates the Excel file differently. So, I had to introduce a new method, to calculate the subtotals before passing onto the ALV method.

      Thanks,

      Juwin

      Author's profile photo Ricky Orea
      Ricky Orea

      ok, thanks Juwin! 🙂

      Author's profile photo Former Member
      Former Member

      Hi Juwin,

      Thank you for the above example. It is very helpful in achieving the ALV tree .

      Can you please explain the aggregation functionality in the ALV method. In my case, aggregation is not happening and after downloading the ALV tree data to excel, only individual cost values are appearing against each row.

      I have used the aggregation function for the nodes. Suppose if a parent has 3 child nodes, then summation of the 3 node values is replacing the parent node with that value.

      Can you please help on this.

      Author's profile photo Juwin Pallipat Thomas
      Juwin Pallipat Thomas
      Blog Post Author

      I am currently on vacation and hence cannot explain this in detail, at this moment.

      But, you cannot use the aggregation functionalities of TREE or ALV to perform aggregation, in this method. Aggregation has to be done programatically before the ALV method is called.

      Thanks

      Juwin

      Author's profile photo Hector Adrian Cardenas Camacho
      Hector Adrian Cardenas Camacho

      Hola juwin, ¿podrías explicar el tema de las agregaciones de columnas? O como lo implementaría yo, porque sale sin los totales.

      Author's profile photo Former Member
      Former Member

      Hello,

      Thank you for this example. How do I download the excel file?

      Thanks,

      Sabal

      Author's profile photo Aidon Li
      Aidon Li

      This piece of code will work for download.

      *&---------------------------------------------------------------------*
      *&      Form  download_file
      *&---------------------------------------------------------------------*
      *       download alv tree to frontend
      *----------------------------------------------------------------------*
      FORM download_file USING pv_xlsx TYPE xstring
      pv_fullpath TYPE string.
      DATA lv_size              TYPE i.
      DATA lt_bintab            TYPE solix_tab.

      * Convert to binary
      CALL FUNCTION 'SCMS_XSTRING_TO_BINARY'
      EXPORTING
      buffer        pv_xlsx
      IMPORTING
      output_length lv_size
      TABLES
      binary_tab    lt_bintab.

      * Save file
      IF lt_bintab IS INITIAL.
      EXIT.
      ENDIF.

      cl_gui_frontend_services=>gui_download(
      EXPORTING
      bin_filesize              lv_size
      filename                  pv_fullpath
      filetype                  'BIN'
      CHANGING
      data_tab                  lt_bintab
      EXCEPTIONS
      file_write_error          1
      no_batch                  2
      gui_refuse_filetransfer   3
      invalid_type              4
      no_authority              5
      unknown_error             6
      header_not_allowed        7
      separator_not_allowed     8
      filesize_not_allowed      9
      header_too_long           10
      dp_error_create           11
      dp_error_send             12
      dp_error_write            13
      unknown_dp_error          14
      access_denied             15
      dp_out_of_memory          16
      disk_full                 17
      dp_timeout                18
      file_not_found            19
      dataprovider_exception    20
      control_flush_error       21
      not_supported_by_gui      22
      error_no_gui              23
      OTHERS                    24
      ).
      IF sy-subrc <> 0.
      MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
      WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4 .
      ENDIF.

      ENDFORM.                    "download_file

      Author's profile photo Pavel Astashonok
      Pavel Astashonok

      Can you please say why xstring_to_xtab does not work in the above xlsx zip export (contrary to SCMS_XSTRING_TO_BINARY) while it works in this file-saving snippet for example?

      Author's profile photo Aidon Li
      Aidon Li

      Hi Juwin,

      I have one issue for this line, it shows dump cause that it cannot found the node and the object is still initial, Could you help for this, Great Thanks.

      *Row elements are under SheetData

      lr_xlnode = lr_file->find_node( ‘sheetData’ ).

      Author's profile photo Aidon Li
      Aidon Li

      Solved by myself. The field name is case sensitive.

       

      This code is pretty good for convert tree alv to excel format - download.

      Author's profile photo Sandra Rossi
      Sandra Rossi

      Thanks. Just reformatting the whole code from the post to help other people to look at it. I also added 3 checkboxes to test everything easily.

      PARAMETERS showtree AS CHECKBOX.
      PARAMETERS showtabl AS CHECKBOX.
      PARAMETERS download AS CHECKBOX.
      
      START-OF-SELECTION.
        DATA: BEGIN OF ls_mara,
                matnr TYPE matnr,
                maktx TYPE matnr,
              END            OF   ls_mara,
              lt_mara LIKE STANDARD TABLE OF ls_mara,
              BEGIN          OF   ls_marc,
                matnr TYPE matnr,
                werks TYPE marc-werks,
                name1 TYPE t001w-name1,
              END            OF   ls_marc,
              lt_marc LIKE STANDARD TABLE OF ls_marc,
              BEGIN          OF   ls_mard,
                matnr TYPE matnr,
                werks TYPE marc-werks,
                lgort TYPE mard-lgort,
                lgobe TYPE t001l-lgobe,
              END            OF   ls_mard,
              lt_mard LIKE STANDARD TABLE OF ls_mard,
              BEGIN          OF   ls_mchb,
                matnr TYPE matnr,
                werks TYPE marc-werks,
                lgort TYPE mard-lgort,
                charg TYPE mchb-charg,
                clabs TYPE labst,
                cumlm TYPE umlmd,
                cinsm TYPE insme,
                ceinm TYPE einme,
                cspem TYPE speme,
                cretm TYPE retme,
              END            OF   ls_mchb,
              lt_mchb LIKE STANDARD TABLE OF ls_mchb,
              BEGIN          OF   ls_output,
                area  TYPE string,
                clabs TYPE labst,
                cumlm TYPE umlmd,
                cinsm TYPE insme,
                ceinm TYPE einme,
                cspem TYPE speme,
                cretm TYPE retme,
              END            OF   ls_output,
              lt_output  LIKE STANDARD TABLE OF ls_output,
              ls_marakey TYPE lvc_nkey,
              ls_marckey TYPE lvc_nkey,
              ls_mardkey TYPE lvc_nkey,
              lr_table   TYPE REF TO cl_salv_table,
              lr_columns TYPE REF TO cl_salv_columns_tree,
              lr_tree    TYPE REF TO cl_salv_tree,
              lr_node    TYPE REF TO cl_salv_node,
              lt_nodes   TYPE salv_t_nodes,
              ls_node    LIKE LINE OF lt_nodes.
        SELECT matnr maktx
               UP TO 50 ROWS
               FROM makt
               INTO TABLE lt_mara
        WHERE spras = sy-langu.
        SELECT matnr a~werks name1
               FROM marc AS a JOIN t001w AS b
                 ON a~werks = b~werks
               INTO TABLE lt_marc
               FOR ALL ENTRIES IN lt_mara
        WHERE matnr = lt_mara-matnr.
        SELECT matnr a~werks a~lgort lgobe
               FROM mard AS a JOIN t001l AS b
                 ON a~lgort = b~lgort
                AND a~werks = b~werks
               INTO TABLE lt_mard
               FOR ALL ENTRIES IN lt_marc
               WHERE matnr = lt_marc-matnr
        AND a~werks = lt_marc-werks.
        SELECT matnr werks lgort charg clabs cumlm cinsm ceinm cspem cretm
               FROM mchb
               INTO TABLE lt_mchb
               FOR ALL ENTRIES IN lt_mard
               WHERE matnr = lt_mard-matnr
                 AND werks = lt_mard-werks
        AND lgort = lt_mard-lgort.
        cl_salv_tree=>factory(
          IMPORTING
            r_salv_tree = lr_tree
          CHANGING
            t_table     = lt_output ).
        lr_columns = lr_tree->get_columns( ).
        lr_columns->set_optimize( abap_true ).
        LOOP AT lt_mara INTO ls_mara.
          CLEAR ls_output.
          CONCATENATE ls_mara-matnr ls_mara-maktx INTO ls_output-area SEPARATED BY space.
          LOOP AT lt_mchb INTO ls_mchb WHERE matnr = ls_mara-matnr.
            ADD-CORRESPONDING ls_mchb TO ls_output.
          ENDLOOP.
          lr_node = lr_tree->get_nodes( )->add_node( related_node = space
                                        data_row     = ls_output
                                        relationship = cl_gui_column_tree=>relat_last_child ).
          ls_marakey = lr_node->get_key( ).
          LOOP AT lt_marc INTO ls_marc WHERE matnr = ls_mara-matnr.
            CLEAR ls_output.
            CONCATENATE ls_marc-werks ls_marc-name1 INTO ls_output-area SEPARATED BY space.
            LOOP AT lt_mchb INTO ls_mchb WHERE matnr = ls_mara-matnr AND werks = ls_marc-werks.
              ADD-CORRESPONDING ls_mchb TO ls_output.
            ENDLOOP.
            lr_node = lr_tree->get_nodes( )->add_node( related_node = ls_marakey
                                          data_row     = ls_output
                                          relationship = cl_gui_column_tree=>relat_last_child ).
            ls_marckey = lr_node->get_key( ).
            LOOP AT lt_mard INTO ls_mard WHERE matnr = ls_mara-matnr AND werks = ls_marc-werks.
              CLEAR ls_output.
              CONCATENATE ls_mard-lgort ls_mard-lgobe INTO ls_output-area SEPARATED BY space.
              LOOP AT lt_mchb INTO ls_mchb WHERE matnr = ls_mara-matnr AND werks = ls_marc-werks
                                          AND lgort = ls_mard-lgort.
                ADD-CORRESPONDING ls_mchb TO ls_output.
              ENDLOOP.
              lr_node = lr_tree->get_nodes( )->add_node( related_node = ls_marckey
                                            data_row     = ls_output
                                            relationship = cl_gui_column_tree=>relat_last_child ).
              ls_mardkey = lr_node->get_key( ).
              LOOP AT lt_mchb INTO ls_mchb WHERE matnr = ls_mara-matnr AND werks = ls_marc-werks
                                          AND lgort = ls_mard-lgort.
                CLEAR ls_output.
                ls_output-area = ls_mchb-charg.
                MOVE-CORRESPONDING ls_mchb TO ls_output.
                lr_node = lr_tree->get_nodes( )->add_node( related_node = ls_mardkey
                                              data_row     = ls_output
                                              relationship = cl_gui_column_tree=>relat_last_child ).
              ENDLOOP.
            ENDLOOP.
          ENDLOOP.
        ENDLOOP.
        IF showtree = 'X'.
          lr_tree->display( ).
        ENDIF.
      
      
        CONSTANTS:lc_xlspace     TYPE c VALUE ' '. "Hexa value for this field should be 0030
        DATA: lv_level TYPE i,
              lv_xlsx  TYPE xstring,
              lt_table TYPE REF TO data,
              lr_data  TYPE REF TO data.
        FIELD-SYMBOLS: <data>  TYPE any,
                       <table> TYPE STANDARD TABLE,
                       <str>   TYPE any.
        lt_nodes = lr_tree->get_nodes( )->get_all_nodes( ).
        LOOP AT lt_nodes INTO ls_node.
          lr_node = ls_node-node.
          CLEAR lv_level.
          DO.
            TRY.
                lr_node = lr_node->get_parent( ).
                ADD 1 TO lv_level.
              CATCH cx_salv_msg.
                EXIT.
            ENDTRY.
          ENDDO.
          lr_data = ls_node-node->get_data_row( ).
          ASSIGN lr_data->* TO <data>.
          IF <table> IS NOT ASSIGNED.
            CREATE DATA lt_table LIKE STANDARD TABLE OF <data>.
            ASSIGN lt_table->* TO <table>.
          ENDIF.
          ASSIGN COMPONENT 1 OF STRUCTURE <data> TO <str>.
          SUBTRACT 1 FROM lv_level.
          DO lv_level TIMES.
            CONCATENATE lc_xlspace <str> INTO <str>.
          ENDDO.
          APPEND <data> TO <table>.
        ENDLOOP.
        cl_salv_table=>factory(
          IMPORTING
            r_salv_table = lr_table
          CHANGING
            t_table = <table> ).
        IF showtabl = 'X'.
          lr_table->display( ).
        ENDIF.
      
        lv_xlsx = lr_table->to_xml( if_salv_bs_xml=>c_type_xlsx ).
        DATA: lr_zip         TYPE REF TO cl_abap_zip,
              lr_xlnode      TYPE REF TO if_ixml_node,
              lr_xldimension TYPE REF TO if_ixml_node,
              lr_xlsheetpr   TYPE REF TO if_ixml_element,
              lr_xloutlinepr TYPE REF TO if_ixml_element,
              lv_file        TYPE xstring,
              lr_file        TYPE REF TO cl_xml_document,
              lr_xlrows      TYPE REF TO if_ixml_node_list,
              lr_xlrow       TYPE REF TO if_ixml_element,
              lr_xlformat    TYPE REF TO if_ixml_element,
              lr_xlworksheet TYPE REF TO if_ixml_element,
              lv_tabix       TYPE i,
              lv_maxlevel    TYPE i,
              lv_levels      TYPE string.
        CREATE OBJECT lr_zip.
        lr_zip->load( lv_xlsx ).
      *Get Worksheet XML file
        lr_zip->get( EXPORTING name = 'xl/worksheets/sheet1.xml'
                     IMPORTING content = lv_file ).
        CREATE OBJECT lr_file.
        lr_file->parse_xstring( lv_file ).
      *Row elements are under SheetData
        lr_xlnode = lr_file->find_node( 'sheetData' ).
        lr_xlrows = lr_xlnode->get_children( ).
        DO lr_xlrows->get_length( ) TIMES.
          lv_tabix = sy-index - 1.
          lr_xlrow ?= lr_xlrows->get_item( lv_tabix ).
      *Find the same node in the SALV Tree object
          READ TABLE lt_nodes INTO ls_node INDEX lv_tabix.
          IF sy-subrc EQ 0.
            lr_node = ls_node-node.
      *Find the level of the node
            CLEAR lv_level.
            DO.
              TRY.
                  lr_node = lr_node->get_parent( ).
                  ADD 1 TO lv_level.
                CATCH cx_salv_msg.
                  EXIT.
              ENDTRY.
            ENDDO.
            SUBTRACT 1 FROM lv_level.
            IF lv_level NE 0.
              lv_levels = lv_level.
              IF lv_level > lv_maxlevel.
                lv_maxlevel = lv_level.
              ENDIF.
              CONDENSE lv_levels.
      *Assign the level to row
              lr_xlrow->set_attribute( name = 'outlineLevel' value = lv_levels ).
              lr_xlrow->set_attribute( name = 'hidden' value = 'true' ).
            ENDIF.
          ENDIF.
        ENDDO.
      *Set maximum levels used in the sheet
        lv_levels = lv_maxlevel.
        CONDENSE lv_levels.
        lr_xlformat ?= lr_file->find_node( 'sheetFormatPr' ).
        lr_xlformat->set_attribute( name = 'outlineLevelRow' value = lv_levels ).
      
      *Create new element in the XML file
        lr_xlworksheet ?= lr_file->find_node( 'worksheet' ).
        lr_xldimension ?= lr_file->find_node( 'dimension' ).
        lr_xlsheetpr = cl_ixml=>create( )->create_document( )->create_element( name = 'sheetPr' ).
        lr_xloutlinepr = cl_ixml=>create( )->create_document( )->create_element( name = 'outlinePr' ).
        lr_xlsheetpr->if_ixml_node~append_child( lr_xloutlinepr ).
        lr_xloutlinepr->set_attribute( name = 'summaryBelow' value = 'false' ).
        lr_xlworksheet->if_ixml_node~insert_child( new_child = lr_xlsheetpr ref_child = lr_xldimension ).
      
      *Create Xstring file for the XML, and add it to Excel Zip file
        lr_file->render_2_xstring( IMPORTING stream = lv_file ).
        lr_zip->delete( EXPORTING name = 'xl/worksheets/sheet1.xml' ).
        lr_zip->add( EXPORTING name = 'xl/worksheets/sheet1.xml'
        content = lv_file ).
      
        lv_xlsx = lr_zip->save( ).
      
        IF download = 'X'.
          PERFORM gui_download USING 'C:\Temp\test.xlsx' lv_xlsx.
        ENDIF.
        ASSERT 1 = 1.
      
      FORM gui_download
            USING
              i_filename     TYPE csequence
              i_file_xstring TYPE xstring.
        DATA lt_xstring TYPE TABLE OF x255.
        DATA l_length TYPE i.
        DATA l_filename TYPE string.
      
        l_filename = i_filename.
      
        CALL METHOD cl_swf_utl_convert_xstring=>xstring_to_table
          EXPORTING
            i_stream = i_file_xstring
          IMPORTING
            e_table  = lt_xstring
          EXCEPTIONS
            OTHERS   = 3.
      
        l_length = xstrlen( i_file_xstring ).
      
        CALL METHOD cl_gui_frontend_services=>gui_download
          EXPORTING
            bin_filesize = l_length
            filename     = l_filename
            filetype     = 'BIN'
          CHANGING
            data_tab     = lt_xstring
          EXCEPTIONS
            OTHERS       = 3.
      ENDFORM.