READ TABLE WITH TABLE KEY does not find record - abap

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.

Related

Parameter type error on BAPI_OBJCL_GETDETAIL call?

lv_objectkey2 = ls_mseg-matnr.
"Transport Category
CALL FUNCTION 'BAPI_OBJCL_GETDETAIL'
EXPORTING
objectkey = lv_objectkey2
objecttable = 'MARA'
classnum = 'Z_MATERIAL_CLASS'
classtype = '001'
TABLES
allocvaluesnum = lt_allocvaluesnum2
allocvalueschar = lt_allocvalueschar2
allocvaluescurr = lt_allocvaluescurr2
return = lt_return2.
READ TABLE lt_allocvaluesnum2 INTO ls_valnum2 WITH KEY charact= 'Z_ADR_QUANTITY'.
IF sy-subrc = 0.
lv_adr_quan = ls_valnum2-value_from + lv_adr_quan.
WRITE: lv_adr_quan TO ls_item-ADR_QUAN EXPONENT 0 DECIMALS 2.
* CONDENSE ls_item-ADR_QUAN.
ENDIF.
Here is my problem : The program gives me that problem : "ADR_QUAN" must be a character-type field (data type C, N, D or T).
I need your opinions to fix the issue or solutions.
CONDENSE Statement works only for the character-like variables. Here, ls_item-ADR_QUAN field is of type Float, That's why you're getting that error.
You can go through the attached documentation link for CONDENSE statement.
CONDENSE Documentation
Even if you want to perform CONDENSE then first, you've to assign ls_item-ADR_QUAN to a character-like variable.

Assigning Field Symbols to Internal Table

I'm trying upload Excel file to internal table in ABAP. I'm using function GUI_UPLOAD and then SCMS_BINARY_TO_XSTRING. At last I have field sybmbol <gt_data> with data from Excel file.
DATA(lo_data_ref) = lo_excel_ref->if_fdt_doc_spreadsheet~get_itab_from_worksheet(
lv_woksheetname ).
*-- Excel work sheet data in dyanmic internal table
ASSIGN lo_data_ref->* TO <gt_data>.
A [CString]
B [CString]
data1
data11
data2
data22
data3
data33
How I can iterate <gt_data> to internal table? I would try like below, but I received dump.
TYPES: BEGIN OF lty_test,
A TYPE string,
B TYPE string,
END OF lty_test.
DATA: lt_test_table TYPE STANDARD TABLE OF lty_test.
As far as I understood, you want to read excel rows with this code.
LOOP AT <gt_data> ASSIGNING FIELD-SYMBOL(<ls_data>).
ENDLOOP.
I am not sure struct of <ls_data> but I think you can read it with index for get to know main idea.
Could you try it like below?
CHECK <gt_data> IS ASSIGNED.
"It's column count for excel file. It can be found dynamically.
DATA(lv_column_count) = 10.
"Loop for rows.
LOOP AT <gt_data> ASSIGNING FIELD-SYMBOL(<ls_data>).
"Loop for columns
DO lv_column_count.
ASSIGN COMPONENT sy-index OF <ls_dat> TO FIELD-SYMBOL(<lfs_value>).
ENDDO.
ENDLOOP.
So first you need to declare a structure for the internal table and each field in the structure should have type "string".
Types:begin of ty_upload,
Field1 type string,
Field2 type string,
End of ty_upload.
Data:it_upload type standard table of ty_upload.
It_upload[] = <gt_data>
Now internal table it_upload should have the data from the field value
Try this:
file = 'C:\xyz.XLS'.
CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
EXPORTING
filename = file
i_begin_col = '1'
i_begin_row = '1'
i_end_col = '5'
i_end_row = '6000'
TABLES
intern = xcel
EXCEPTIONS
inconsistent_parameters = 1
upload_ole = 2
OTHERS = 3.
LOOP AT xcel.
" xcel is an internal table and has field xcel-value
ENDLOOP.

Fill arbitrary column in dynamic itab?

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

Creating a range for a field from internal table using RTTS

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.

Create dynamic ABAP internal table

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.