On selection screen, the user needs to insert a table name, and I need to get first 3 fields from that table and display them in an ALV for the output. What I understand from reading tutorials is that I need to call method cl_alv_table_create=>create_dynamic_table, but I don't know how to create the fieldcatalog.
DATA: t_newtable TYPE REF TO data,
t_fldcat TYPE lvc_t_fcat,
CALL METHOD cl_alv_table_create=>create_dynamic_table
EXPORTING
it_fieldcatalog = t_fldcat
IMPORTING
ep_table = t_newtable.
I assume that the table name which user enters is a data dictionary table (like SFLIGHT). If yes, then you can generate the field catalog as follows.
data : it_tabdescr type abap_compdescr_tab,
wa_tabdescr type abap_compdescr.
data : ref_table_descr type ref to cl_abap_structdescr.
ref_table_descr ?= cl_abap_typedescr=>describe_by_name( p_table ).
it_tabdescr[] = ref_table_descr->components[].
loop at it_tabdescr into wa_tabdescr.
clear wa_fieldcat.
wa_fieldcat-fieldname = wa_tabdescr-name .
wa_fieldcat-datatype = wa_tabdescr-type_kind.
wa_fieldcat-inttype = wa_tabdescr-type_kind.
wa_fieldcat-intlen = wa_tabdescr-length.
wa_fieldcat-decimals = wa_tabdescr-decimals.
append wa_fieldcat to it_fieldcat.
endloop.
Here, "p_table" is the selection screen parameter containing the table
name.
Related
I am currently trying to create a report with a dynamically created internal table (the number of columns can be different every time).
Is there a way how I can address the generated columns while filling the structure of the given table?
Here is the code I am working with:
FIELD-SYMBOLS: <fcat> TYPE lvc_s_fcat,
<fcat_aus> TYPE ANY TABLE.
IF so_datum-high <> ''.
DATA(lv_month_diff) = so_datum-high - so_datum-low.
ELSE.
DATA(lv_month) = so_datum-low.
ENDIF.
APPEND INITIAL LINE TO gt_fcat ASSIGNING <fcat>.
<fcat>-fieldname = 'MATNR'.
<fcat>-tabname = 'GZ_TABLE'.
<fcat>-ref_field = 'MATNR'.
<fcat>-ref_table = 'MAKT'.
APPEND INITIAL LINE TO gt_fcat ASSIGNING <fcat>.
<fcat>-fieldname = 'MAKTX'.
<fcat>-tabname = 'GZ_TABLE'.
<fcat>-ref_field = 'MAKTX'.
<fcat>-ref_table = 'MAKT'.
DATA(lv_counter) = 1.
DO 10 TIMES.
DATA(lv_fieldname_qt) = 'MOQ' && lv_counter.
DATA(lv_fieldname_fqt) = 'MFQ' && lv_counter.
lv_counter = lv_counter + 1.
APPEND INITIAL LINE TO gt_fcat ASSIGNING <fcat>.
<fcat>-fieldname = lv_fieldname_qt.
<fcat>-tabname = 'GZ_TABLE'.
<fcat>-ref_field = 'MNG01'.
<fcat>-ref_table = 'MDEZ'.
APPEND INITIAL LINE TO gt_fcat ASSIGNING <fcat>.
<fcat>-fieldname = lv_fieldname_fqt.
<fcat>-tabname = 'GZ_TABLE'.
<fcat>-ref_field = 'MNG01'.
<fcat>-ref_table = 'MDEZ'.
ENDDO.
CALL METHOD cl_alv_table_create=>create_dynamic_table
EXPORTING
it_fieldcatalog = gt_fcat
IMPORTING
ep_table = gz_table
EXCEPTIONS
generate_subpool_dir_full = 1
OTHERS = 2.
ASSIGN gz_table->* to <fcat_aus>.
Maybe one of you has an idea.
Thanks in advance!
Use the ASSIGN COMPONENT statement to access a structure component dynamically. Check the ABAP documentation (F1) for further details. You can specify the component by index or by field name.
Here is an example to complete Thomas answer (note that I don't explain how to create a table dynamically with CREATE DATA as it's not your question, here it's created statically with 2 components, I only explain how to fill an internal table by referring to it dynamically):
TYPES: BEGIN OF ty_line,
comp1 TYPE i,
text_component TYPE string,
END OF ty_line,
ty_itab TYPE STANDARD TABLE OF ty_line WITH DEFAULT KEY.
DATA r_itab TYPE REF TO DATA.
DATA r_line TYPE REF TO DATA.
FIELD-SYMBOLS <itab> TYPE STANDARD TABLE.
FIELD-SYMBOLS <line> TYPE ANY.
FIELD-SYMBOLS <component> TYPE ANY.
" create an internal table, here it's the static way, but you may also use CREATE DATA
" to create it dynamically
CREATE DATA r_itab TYPE ty_itab.
ASSIGN r_itab->* TO <itab>.
" now let's fill it dynamically, first define a line
CREATE DATA r_line LIKE LINE OF <itab>.
ASSIGN r_line->* TO <line>.
ASSIGN COMPONENT 1 OF <line> TO <component>. " access to COMP1 component
IF sy-subrc = 0.
<component> = 30.
ENDIF.
ASSIGN COMPONENT 'TEXT_COMPONENT' OF <line> TO <component>.
IF sy-subrc = 0.
<component> = 'text'.
ENDIF.
" now add the line
INSERT <line> INTO TABLE <itab>.
Note that it's possible to access the whole line with ASSIGN COMPONENT 0 ... (especially useful if the internal table has none component).
More information:
ASSIGN
->* (dereferencing operator)
CREATE DATA
I'm trying to use the class /ui5/cl_json_parser for parsing a JSON string.
The following code snippet reproduces the problem:
REPORT ztest_json_parse.
DATA: input TYPE string,
output TYPE string,
json_parser TYPE REF TO /ui5/cl_json_parser.
input = '{"address":[{"street":"Road","number":"545"},{"street":"Avenue","number":"15"}]}'.
CREATE OBJECT json_parser.
json_parser->parse( input ).
json_parser->print( ).
output = json_parser->value( path = '/address/1/street' ).
WRITE output.
The print method shows the correct parsed JSON string, but the output variable is always empty.
I have traced the code down to the method VALUE of the class /UI5/CL_JSON_PARSER, at line 15, which contains:
read table m_entries into l_entry with table key parent = l_parent name = l_name.
In the debugger, I can see that l_parent = '/address/1' and l_name = 'street', and that the internal table m_entries contains a record with parent = '/address/1' and name = 'street'. Nevertheless the READ statement always returns sy-subrc = 4 and does not find anything.
Can anyone help?
First: Do not use /ui5/cl_json_parser class, it is intended for internal use ONLY and has no reliable documentation
Secondly, here is the sample how you can fetch street value from the first element of your JSON:
DATA(o_json) = cl_abap_codepage=>convert_to( '{"address":[{"street":"Road","number":"545"},{"street":"Avenue","number":"15"}]' ).
DATA(o_reader) = cl_sxml_string_reader=>create( o_json ).
TRY.
DATA(o_node) = o_reader->read_next_node( ).
WHILE o_node IS BOUND.
DATA(op) = CAST if_sxml_open_element( o_node ).
LOOP AT op->get_attributes( ) ASSIGNING FIELD-SYMBOL(<a>).
DATA(attr) = <a>->get_value( ).
ENDLOOP.
IF attr <> 'street'.
o_node = o_reader->read_next_node( ).
ELSE.
DATA(val) = CAST if_sxml_value_node( o_reader->read_next_node( ) ).
WRITE: '/address/1/street =>', val->get_value( ).
EXIT.
ENDIF.
ENDWHILE.
CATCH cx_root INTO DATA(e_txt).
ENDTRY.
As far as I know, there is no class in ABAP that allows fetching single JSON attributes like XPath.
Certainly agree with Suncatcher on avoid UI5 Json parser.
If you dont control/know the structure of the source data, Suncatchers answer is good.
However,
if you know the basic structure of the source JSON and you must, if you plan to access the first address row, fieldname street .
AND you can have the source provided using uppercase variable names then you can use the so called identity transformation.
types: begin of ty_addr,
street type string,
number type string,
end of ty_addr.
types ty_addr_t type STANDARD TABLE OF ty_addr.
DATA: input TYPE string,
ls_addr TYPE ty_addr,
lt_addr type ty_addr_t.
input = '{"ADDRESS":[{"STREET":"Road","NUMBER":"545"},{"STREET":"Avenue","NUMBER":"15"}]}'.
CALL TRANSFORMATION id SOURCE XML input
RESULT address = lt_addr.
read table lt_addr index 1 into ls_addr.
WRITE ls_addr-street.
I want to create a function/custom class method that takes in 2 parameters:
1) IM_ITAB type ANY TABLE
2) IM_COMPONENT type STRING
and returns 1 parameter:
1) EX_RANGE type PIQ_SELOPT_T
So, algorithm is like this:
First of all, we check if the column with a component name at all exists
Then, we check that internal table is not empty.
Then, we loop through internal table assigning component and filling range table. Code is below.
METHODS compose_range_from_itab
IMPORTING
IM_ITAB type ANY TABLE
IM_COMPONENT type STRING
EXPORTING
EX_RANGE type PIQ_SELOPT_T.
...
METHOD compose_range_from_itab.
DATA: lo_obj TYPE REF TO cl_abap_tabledescr,
wa_range TYPE selopt,
lt_range TYPE piq_selopt_t.
FIELD-SYMBOLS: <fs_line> TYPE ANY,
<fs_component> TYPE ANY.
lo_obj ?= cl_abap_typedescr=>describe_by_data( p_data = im_itab ).
READ TABLE lo_obj->key TRANSPORTING NO FIELDS WITH KEY name = im_component.
IF sy-subrc IS INITIAL.
IF LINES( im_itab ) GT 0.
LOOP AT im_itab ASSIGNING <fs_line>.
ASSIGN COMPONENT im_component OF STRUCTURE <fs_line> TO <fs_component>.
wa_range-sign = 'I'.
wa_range-option = 'EQ'.
wa_range-low = <fs_component>.
APPEND wa_range TO lt_range.
ENDLOOP.
SORT lt_range BY low.
DELETE ADJACENT DUPLICATES FROM lt_range COMPARING low.
ex_range[] = lt_range[].
ENDIF.
ENDIF.
ENDMETHOD.
But I want to improve the method further. If the imported internal table has, let's say, 255 columns, then it will take longer to loop through such table. But I need only one column to compose the range.
So I want to get components of internal table, then choose only one component, create a new line type containing only that component, then create internal table with that line type and copy.
Here is the pseudo code corresponding to what I want to achieve:
append corresponding fields of im_itab into new_line_type_internal_table.
How can I "cut out" one component and create a new line type using RTTS?
You are overcomplicating everything, you don't need RTTS for that.
DEFINE make_range.
ex_range = VALUE #( BASE ex_range ( sign = 'I' option = 'EQ' low = &1 ) ).
END-OF-DEFINITION.
LOOP AT im_itab ASSIGNING FIELD-SYMBOL(<fs_line>).
ASSIGN COMPONENT im_component OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<fs_field>).
CHECK sy-subrc = 0 AND <fs_field> IS NOT INITIAL.
make_range <fs_field>.
ENDLOOP.
And yes, as Sandra said, you won't gain any performance with RTTS, just the opposite.
Surprisingly, this variant turned out to be faster:
CLASS-METHODS make_range_variant_2
IMPORTING
sample TYPE table_type
column TYPE string
RETURNING
VALUE(result) TYPE range_type.
METHOD make_range_variant_2.
TYPES:
BEGIN OF narrow_structure_type,
content TYPE char32,
END OF narrow_structure_type.
TYPES narrow_table_type TYPE STANDARD TABLE OF narrow_structure_type WITH EMPTY KEY.
DATA narrow_table TYPE narrow_table_type.
DATA(mapping) =
VALUE cl_abap_corresponding=>mapping_table_value(
( kind = cl_abap_corresponding=>mapping_component srcname = column dstname = 'CONTENT' ) ).
DATA(mover) =
cl_abap_corresponding=>create_with_value(
source = sample
destination = narrow_table
mapping = mapping ).
mover->execute(
EXPORTING
source = sample
CHANGING
destination = narrow_table ).
LOOP AT narrow_table ASSIGNING FIELD-SYMBOL(<row>).
INSERT VALUE #(
sign = 'I'
option = 'EQ'
low = <row>-content )
INTO TABLE result.
ENDLOOP.
ENDMETHOD.
CL_ABAP_CORRESPONDING delegates to a kernel function for the structure-to-structure move, which apparently is faster than the ABAP-native ASSIGN COMPONENT [...] OF STRUCTURE [...] TO FIELD-SYMBOL [...]. The actual loop then seems to be faster because it uses fixed-name assignments.
Maybe somebody could verify.
I would not go for a Macro.
Data:
lr_data type ref to data.
FIELD-SYMBOLS:
<lv_component> TYPE any,
<ls_data> TYPE any.
CREATE DATA lr_data LIKE LINE OF im_itab.
ASSIGN lr_data->* TO <ls_data>.
"Check whether im_component exists
ASSIGN COMPONENT im_component OF STRUCTURE <ls_data> TO <lv_component>.
CHECK sy-subrc EQ 0.
LOOP AT im_itab INTO <ls_data>.
APPEND VALUE #( sign = 'I' option = 'EQ' low = <lv_component> ) TO ex_range.
ENDLOOP.
How to update Z fields with BAPI_OUTB_DELIVERY_CHANGE using EXTENSION2?
I tried to update some Z fields in LIKP table using the BAPI to update deliveries but after call it any changes wasn't made.
Any suggestions?
I found the BAdI - SMOD_V50B0001. I made an enhancement implementaction and in the method EXIT_SAPLV50I_010 (User Exit BAPI for Changes to Outbound Delivery) set the follow code.
METHOD if_ex_smod_v50b0001~exit_saplv50i_010.
CONSTANTS: lc_vbkok TYPE char5 VALUE 'VBKOK', "Enhance the structure for more z fields in LIKP
lc_vbpok TYPE char5 VALUE 'VBPOK', "Enhance the structure for more z fields in LIPS
lc_cs_vbkok TYPE char8 VALUE 'CS_VBKOK'.
DATA: lv_item TYPE posnr_vl.
FIELD-SYMBOLS: <lfs_fieldname> TYPE any.
LOOP AT extension2 INTO DATA(lw_extension2).
CASE lw_extension2-param.
"As a structure in row 0
WHEN lc_vbkok. "Fields for LIKP
ASSIGN (lc_cs_vbkok) TO FIELD-SYMBOL(<lfs_vbkok>).
IF <lfs_vbkok> IS ASSIGNED.
ASSIGN COMPONENT lw_extension2-field OF STRUCTURE <lfs_vbkok> TO <lfs_fieldname>.
IF <lfs_fieldname> IS ASSIGNED.
<lfs_fieldname> = lw_extension2-value.
ENDIF.
ENDIF.
"As a table from row 1 to n
WHEN lc_vbpok. "Fields for LIPS
MOVE lw_extension2-row TO lv_item.
READ TABLE ct_vbpok ASSIGNING FIELD-SYMBOL(<lfs_vbpok>) WITH KEY posnr_vl = lv_item.
IF sy-subrc = 0.
ASSIGN COMPONENT lw_extension2-field OF STRUCTURE <lfs_vbpok> TO <lfs_fieldname>.
IF <lfs_fieldname> IS ASSIGNED.
<lfs_fieldname> = lw_extension2-value.
ENDIF.
ENDIF.
ENDCASE.
ENDLOOP.
ENDMETHOD.
Enhace the structures VBKOK as well LIKP with appen structures to add new z fields and enhance the structure VBPOK as well LIPS for the same purpose.
Also take a look to the program LV50LF01 and would need to implement an additional enhancement point to keep the values.
After this a think that I got it.
METHOD m_update_delivery.
DATA: lw_header_data TYPE bapiobdlvhdrchg,
lw_header_control TYPE bapiobdlvhdrctrlchg,
lw_extension2 TYPE bapiext.
DATA: li_extension2 TYPE TABLE OF bapiext,
li_return TYPE TABLE OF bapiret2.
DATA: lv_delivery TYPE bapiobdlvhdrchg-deliv_numb.
lw_header_data-deliv_numb = p_w_output-delivery.
lw_header_control-deliv_numb = p_w_output-delivery.
lv_delivery = p_w_output-delivery.
lw_extension2-param = 'VBKOK'.
lw_extension2-field = 'ZZEXIDV'.
lw_extension2-value = p_w_output-value.
APPEND lw_extension2 TO li_extension2.
" Call BAPI to update delivery
CALL FUNCTION 'BAPI_OUTB_DELIVERY_CHANGE'
EXPORTING
header_data = lw_header_data
header_control = lw_header_control
delivery = lv_delivery
TABLES
extension2 = li_extension2
return = li_return.
READ TABLE li_return INTO DATA(lw_return)
WITH KEY type = c_e.
IF sy-subrc <> 0.
COMMIT WORK AND WAIT.
ENDIF.
ENDMETHOD.
You need to add BAPI_COMMIT after your BAPI function module.
I am pasting this program for example but i will never know the type of the table (here vbap and vbak).
My goals is to display my field symbol without knowing the types.
Is it possible ?
Here is my code :
REPORT ZTEST_FME_FOL.
type-pools slis .
FIELD-SYMBOLS : <mytable> TYPE ANY TABLE.
DATA : lv_alv_table TYPE REF TO cl_salv_table,
lv_funct TYPE REF TO cl_salv_functions,
lv_columns TYPE REF TO cl_salv_columns_table,
lv_column TYPE REF TO CL_SALV_COLUMN_table.
SELECT * from vbap INNER JOIN VBAK ON vbap~vbeln = vbak~vbeln UP TO 10 ROWS INTO TABLE <mytable>.
TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = lv_alv_table
CHANGING
t_table = <mytable> ).
CATCH cx_salv_msg .
ENDTRY.
lv_funct = lv_alv_table->get_functions( ).
lv_funct->set_all( Abap_True ).
lv_columns = lv_alv_table->get_columns( ).
lv_alv_table->display( ).
Thanks in advance !
Depending on what you 're trying to do there's going to be more validation required than what I've done, but in essence this is what you need.
Using (dynamic) joins may be particularly tricky.
report zevw_test_dynamic_alv.
parameters: p_table type string obligatory.
field-symbols: <gt_table> type standard table.
data: gt_data type ref to data.
start-of-selection.
create data gt_data type table of (p_table).
assign gt_data->* to <gt_table>.
select * from (p_table) up to 10 rows
into table <gt_table>.
perform display_results using <gt_table>. "Your ALV stuff will be in here
You may even have to build the fieldcat manually and then use
call method cl_alv_table_create=>create_dynamic_table
exporting
it_fieldcatalog = gt_fieldcat[]
importing
ep_table = gt_data.
to get the data reference