-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for ABAP #11
Comments
Hi, this should do the trick. I'm pretty sure most people working in SAP have access to a system with MM and therefore the MARA table for the demo code at the bottom, and even if they dont, they can just swap out some the SQL and use whatever other table they fancy. Keep in mind that this implementation is fairly slow because SAP doesnt easily support the packing of strings into bytestrings (and vice versa). Ive played around with evil SYSTEM-CALL wizardry that can make reading and writing the data faster (albeit at the cost of not packing the binary, leaving the "00" at the end of most character's byte representations and thus almost doubling the total size of the binary), but that would probably never make it to production code, unless someone official from SAP wants to start supporting RSV, because using those commands is (officially speaking) forbidden in client code. As for the "non-tabular data" in the specification, you can't really represent that in ABAP on a technical level because its a very statically typed language, so just creating a data object that dynamically fits "non-tabular data" is an annoying task because you'd have to create a whole program at runtime that exactly defines the specific data you want to put in it, which completely defeats the purpose of it all. So yeah, I kinda had to omit that, because there really isn't a clever workaround that could be used en masse. I also made sure to overengineer the decoder a little so it may use the header names for the correct mapping to the corresponding fields in the target table. As for compatibility, this works all the way down until 7.31. That's the oldest ABAP version I can work on. REPORT.
CLASS lcl_rsv DEFINITION.
PUBLIC SECTION.
"I'm just gonna assume whoever calls this isnt giving me something stupid like:
" - a nested table
" - reference types as columns
" - an elementary line type (like, just make a structure with one element if you need that, sheesh)
" - the wrong parameters regarding whether or not there is or should be a header
CLASS-METHODS:
encode
IMPORTING
it_data TYPE INDEX TABLE
iv_codepage TYPE abap_encod DEFAULT '4110' "UTF-8
iv_add_header TYPE boolean OPTIONAL
EXPORTING
ev_result TYPE xstring,
decode
IMPORTING
iv_rsv TYPE xsequence
iv_codepage TYPE cpcodepage DEFAULT '4110' "UTF-8
iv_has_header TYPE boolean OPTIONAL
iv_use_header TYPE boolean OPTIONAL
EXPORTING
et_data TYPE INDEX TABLE.
PRIVATE SECTION.
CLASS-METHODS:
get_flat_table_components
IMPORTING
it_data TYPE INDEX TABLE
EXPORTING
et_components TYPE abap_component_tab.
CONSTANTS: BEGIN OF gc_byte,
eov TYPE x LENGTH 1 VALUE 255, "End of value
null TYPE x LENGTH 1 VALUE 254, "Only useful for decoding, there is no nullable non-reference type in ABAP
eol TYPE x LENGTH 1 VALUE 253, "End of line
END OF gc_byte.
ENDCLASS.
CLASS lcl_rsv IMPLEMENTATION.
METHOD encode.
DATA: lt_components TYPE abap_component_tab,
lv_string TYPE string,
lv_xstring TYPE xstring,
lv_num_components TYPE i.
FIELD-SYMBOLS: <ls_component> LIKE LINE OF lt_components,
<gv_string> TYPE any,
<gv_xstring> TYPE any,
<is_data> TYPE any,
<iv_any> TYPE any.
DEFINE push_to_xstring.
lv_string = &1.
lv_xstring = cl_bcs_convert=>string_to_xstring( iv_codepage = iv_codepage iv_string = lv_string ).
END-OF-DEFINITION.
CLEAR ev_result.
"Get the columns
get_flat_table_components(
EXPORTING
it_data = it_data
IMPORTING
et_components = lt_components ).
"Optional header
IF iv_add_header = abap_true.
LOOP AT lt_components ASSIGNING <ls_component>.
push_to_xstring <ls_component>-name.
CONCATENATE ev_result lv_xstring gc_byte-eov INTO ev_result IN BYTE MODE.
ENDLOOP.
CONCATENATE ev_result gc_byte-eol INTO ev_result IN BYTE MODE.
ENDIF.
"Now the data
lv_num_components = lines( lt_components ).
CLEAR lt_components.
LOOP AT it_data ASSIGNING <is_data>.
DO lv_num_components TIMES.
ASSIGN COMPONENT sy-index OF STRUCTURE <is_data> TO <iv_any>.
push_to_xstring <iv_any>.
CONCATENATE ev_result lv_xstring gc_byte-eov INTO ev_result IN BYTE MODE.
ENDDO.
CONCATENATE ev_result gc_byte-eol INTO ev_result IN BYTE MODE.
ENDLOOP.
ENDMETHOD.
METHOD decode.
DATA: lt_components TYPE abap_component_tab,
lv_num_components TYPE i,
lt_rsv TYPE TABLE OF xstring,
lt_line_values TYPE TABLE OF xstring,
lt_mixmap TYPE TABLE OF i,
lv_string TYPE string,
lv_xstring TYPE xstring.
FIELD-SYMBOLS: <gv_string> TYPE any,
<gv_xstring> TYPE any,
<lv_rsv> TYPE xstring,
<lv_value> TYPE xstring,
<es_data> TYPE any,
<ev_any> TYPE any,
<lv_mixmap> LIKE LINE OF lt_mixmap.
DEFINE push_to_string.
lv_xstring = &1.
lv_string = cl_bcs_convert=>xstring_to_string( iv_cp = iv_codepage iv_xstr = lv_xstring ).
END-OF-DEFINITION.
CLEAR et_data.
"Get the columns
get_flat_table_components(
EXPORTING
it_data = et_data
IMPORTING
et_components = lt_components ).
"Get the basic table form
SPLIT iv_rsv AT gc_byte-eol INTO TABLE lt_rsv IN BYTE MODE.
"Delete the header line before processing the data
IF iv_use_header = abap_true OR iv_has_header = abap_true.
"Decode the header line and then create the mapping table
IF iv_use_header = abap_true.
READ TABLE lt_rsv INDEX 1 ASSIGNING <lv_rsv>.
SPLIT <lv_rsv> AT gc_byte-eov INTO TABLE lt_line_values IN BYTE MODE.
LOOP AT lt_line_values ASSIGNING <lv_value>.
push_to_string <lv_value>.
READ TABLE lt_components TRANSPORTING NO FIELDS WITH KEY name = lv_string.
IF sy-subrc = 0.
APPEND sy-tabix TO lt_mixmap.
ELSE.
APPEND INITIAL LINE TO lt_mixmap.
ENDIF.
DELETE lt_line_values.
ENDLOOP.
ENDIF.
DELETE lt_rsv INDEX 1.
ENDIF.
"Now the data
lv_num_components = lines( lt_components ).
CLEAR lt_components.
"Unchanged order, make a simple mapping table
IF lt_mixmap IS INITIAL.
DO lv_num_components TIMES.
APPEND sy-index TO lt_mixmap.
ENDDO.
ENDIF.
"Failsafe if someone actually wants to add nulls (which are the same as empty values to us)
REPLACE ALL OCCURRENCES OF gc_byte-null IN TABLE lt_rsv WITH gc_byte-eov IN BYTE MODE.
"Start mapping the data around
LOOP AT lt_rsv ASSIGNING <lv_rsv>.
SPLIT <lv_rsv> AT gc_byte-eov INTO TABLE lt_line_values IN BYTE MODE.
APPEND INITIAL LINE TO et_data ASSIGNING <es_data>.
LOOP AT lt_mixmap ASSIGNING <lv_mixmap> WHERE table_line IS NOT INITIAL.
ASSIGN COMPONENT <lv_mixmap> OF STRUCTURE <es_data> TO <ev_any>.
READ TABLE lt_line_values INDEX sy-tabix ASSIGNING <lv_value>.
push_to_string <lv_value>.
<ev_any> = lv_string.
ENDLOOP.
DELETE lt_rsv.
ENDLOOP.
ENDMETHOD.
METHOD get_flat_table_components.
DATA: lo_tabledescr TYPE REF TO cl_abap_tabledescr,
lo_structdescr TYPE REF TO cl_abap_structdescr,
lo_includedescr LIKE lo_structdescr,
lt_include_components LIKE et_components.
FIELD-SYMBOLS <es_component> LIKE LINE OF et_components.
lo_tabledescr ?= cl_abap_tabledescr=>describe_by_data( it_data ).
lo_structdescr ?= lo_tabledescr->get_table_line_type( ).
et_components = lo_structdescr->get_components( ).
LOOP AT et_components ASSIGNING <es_component> WHERE as_include = abap_true.
lo_includedescr ?= <es_component>-type.
lt_include_components = lo_includedescr->get_components( ).
DELETE et_components.
INSERT LINES OF lt_include_components INTO et_components INDEX sy-tabix.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
"Use whatever table you like to test it, i suppose something like a BI system doesnt have the MARA
DATA: gt_mara TYPE TABLE OF mara,
gv_result TYPE xstring.
SELECT * FROM mara UP TO 300 ROWS INTO TABLE gt_mara.
lcl_rsv=>encode(
EXPORTING
it_data = gt_mara
iv_add_header = abap_true
IMPORTING
ev_result = gv_result ).
CLEAR gt_mara.
"If it properly decodes the data should land back here in gt_mara
lcl_rsv=>decode(
EXPORTING
iv_rsv = gv_result
iv_has_header = abap_true
IMPORTING
et_data = gt_mara ).
"Bonus test: mixed order.
TYPES: BEGIN OF gy_weird_mara,
mstae TYPE mara-mstae,
matnr TYPE matnr,
loekz TYPE mara-lvorm,
END OF gy_weird_mara.
DATA gt_weird_mara TYPE TABLE OF gy_weird_mara.
lcl_rsv=>decode(
EXPORTING
iv_rsv = gv_result
iv_use_header = abap_true
IMPORTING
et_data = gt_weird_mara ).
BREAK-POINT. |
I always wanted to take a stab at a coding challenge. I wrote this up on a sandbox system running Netweaver 7.50. CLASS zcl_rsv_util DEFINITION
PUBLIC FINAL
CREATE PUBLIC.
PUBLIC SECTION.
CONSTANTS end_of_value TYPE xstring VALUE 'FF'.
CONSTANTS null_value TYPE xstring VALUE 'FE'.
CONSTANTS end_of_row TYPE xstring VALUE 'FD'.
DATA rsv TYPE STANDARD TABLE OF xstringtab.
DATA rsv_decoded TYPE STANDARD TABLE OF stringtab.
TYPES encode_tab TYPE STANDARD TABLE OF stringtab.
METHODS decode_rsv
IMPORTING rsv_xstring TYPE xstring.
METHODS encode_rsv
IMPORTING encode_tab TYPE encode_tab
RETURNING VALUE(rsv_xstring) TYPE xstring.
ENDCLASS.
CLASS zcl_rsv_util IMPLEMENTATION.
METHOD decode_rsv.
DATA rows TYPE xstringtab.
DATA row_strings TYPE stringtab.
DATA row_xstrings TYPE xstringtab.
SPLIT rsv_xstring AT end_of_row INTO TABLE rows IN BYTE MODE.
LOOP AT rows ASSIGNING FIELD-SYMBOL(<row>).
CLEAR row_strings.
SPLIT <row> AT end_of_value INTO TABLE row_xstrings IN BYTE MODE.
LOOP AT row_xstrings ASSIGNING FIELD-SYMBOL(<row_xstring>).
IF <row_xstring> = null_value.
CLEAR <row_xstring>.
ENDIF.
APPEND cl_abap_codepage=>convert_from( source = <row_xstring> ) TO row_strings.
ENDLOOP.
APPEND row_xstrings TO rsv.
APPEND row_strings TO rsv_decoded.
ENDLOOP.
ENDMETHOD.
METHOD encode_rsv.
LOOP AT encode_tab ASSIGNING FIELD-SYMBOL(<encode_row>).
LOOP AT <encode_row> ASSIGNING FIELD-SYMBOL(<encode_value>).
rsv_xstring = |{ rsv_xstring }{ cl_abap_codepage=>convert_to( source = <encode_value> ) }{ end_of_value }|.
ENDLOOP.
rsv_xstring = |{ rsv_xstring }{ end_of_row }|.
ENDLOOP.
ENDMETHOD.
ENDCLASS. |
ABAP implementation requested
The text was updated successfully, but these errors were encountered: