I am having a fieldsymbol with type = data
FIELD-SYMBOLS: <fs_it_orignal> TYPE data.
Then I fill dynamically fill the field symbol<fs_it_orignal> = some_values.
this some_value can be either of structure A B or C .
I was looking for the best way to check where the <fs_it_orignal> is of structure A B or C.
If you are just after knowing if the field symbol is of a certain type, you can do this by comparing the name of structure:
field-symbols: <fs_it_orignal> type any.
"-- Code here to assign your FS to something
case replace( val = cl_abap_typedescr=>describe_by_data( <fs_it_orignal> )->absolute_name
regex = '\\TYPE=' with = '' ).
when 'Z_STRUCT_A'.
"-- Do what ever
when 'Z_STRUCT_B'.
"-- Do what ever
when 'Z_STRUCT_C'.
"-- Do what ever
endcase.
Related
If I have a tabkey value, e.g., DATA(lv_tabkey) = '1000041508773180000013000'., which is the concatenated value of all table keys for an entry and I know the name of the corresponding table:
How I can get the table entry for it without splitting tabkey manually and therefore having to write the order and length of each key field?
Full example:
" The first 3 chars always belong to the 'mandt' field
" which can't be filtered in the SELECT, therefore
" I ignore it and start with key2
DATA(lv_tabkey) = '1000041508773180000013000'.
"ToDo - how to make this generic? - START
DATA(lv_key2) = lv_tabkey+3(12).
DATA(lv_key3) = lv_tabkey+15(3).
DATA(lv_key4) = lv_tabkey+18(4).
DATA(lv_key5) = lv_tabkey+22(3).
DATA(lv_where) = 'key2 = ' && lv_key2 &&
' AND key3 = ' && lv_key3 &&
' AND key4 = ' && lv_key4 &&
' AND key5 = ' && lv_key5.
"ToDo - how to make this generic? - END
SELECT *
FROM table_x
INTO TABLE DATA(lt_results)
WHERE (lv_where).
I think I have to somehow iterate over the table fields, find out the keys and their length - but I don't know how to do this.
The statement you are seeking is:
ASSIGN tabkey TO < structure> CASTING TYPE HANDLE r_type_struct.
Knowing type handle for the (table key) structure you can fill it with values in a generic way and query the table using the structure. Here is how:
DATA: handle TYPE REF TO data,
lref_struct TYPE REF TO cl_abap_structdescr.
FIELD-SYMBOLS: <key_fld> TYPE abap_componentdescr.
SELECT * UP TO 5000 ROWS
FROM cdpos
INTO TABLE #DATA(t_cdpos)
WHERE tabname NOT LIKE '/%'.
LOOP AT t_cdpos ASSIGNING FIELD-SYMBOL(<fs_cdpos>).
lref_struct ?= cl_abap_structdescr=>describe_by_name( <fs_cdpos>-tabname ).
* get key fields
DATA(key_fields) = VALUE ddfields( FOR line IN lref_struct->get_ddic_field_list( ) WHERE ( keyflag NE space ) ( line ) ).
* filling key field components
DATA(key_table) = VALUE abap_component_tab( FOR ls_key IN key_fields
( name = ls_key-fieldname
type = CAST #( cl_abap_datadescr=>describe_by_name( ls_key-domname ) )
)
).
* create key fields type handle
TRY.
DATA(r_type_struct) = cl_abap_structdescr=>create( key_table ).
CATCH cx_sy_struct_creation .
ENDTRY.
* create key type
CHECK r_type_struct IS NOT INITIAL.
CREATE DATA handle TYPE HANDLE r_type_struct.
ASSIGN handle->* TO FIELD-SYMBOL(<structure>).
* assigning final key structure
ASSIGN <fs_cdpos>-tabkey TO <structure> CASTING TYPE HANDLE r_type_struct.
* filling values
LOOP AT key_table ASSIGNING <key_fld>.
ASSIGN COMPONENT <key_fld>-name OF STRUCTURE <structure> TO FIELD-SYMBOL(<val>).
CHECK sy-subrc = 0.
<key_fld>-suffix = <val>.
ENDLOOP.
DATA(where_cond) = REDUCE string( INIT where = ` ` FOR <field> IN key_table WHERE ( name <> 'MANDT' ) NEXT where = where && <field>-name && ` = '` && <field>-suffix && `' AND ` ).
where_cond = substring( val = where_cond off = 0 len = strlen( where_cond ) - 4 ).
IF <fs_cdpos>-tabname = 'BNKA'.
SELECT *
INTO TABLE #DATA(lt_bnka)
FROM bnka
WHERE (where_cond).
ENDIF.
ENDLOOP.
Here I built the sample on table CDPOS that contain table names and additionally concatenated key values in field tabkey, in other words exactly what you are trying to use.
In a loop it detects table types, builds the key and make SQL query in a generic way. Here I used table BNKA for simplicity, but SQL SELECT can be generized as well via field-symbol. Also I made a trick by filling values into the same tab that contains structure components, in SUFFIX field.
P.S. Before passing where condition into query make proper data type validation to avoid such errors as SAPSQL_DATA_LOSS, because with new syntax it makes a strict check.
your use case reminds me that how I deal with Change Document key.(CDHDR/CDPOS).
Hope it helps!
DATA:
lv_tabkey TYPE char50,
ls_table TYPE table_x.
FIELD-SYMBOLS:
<ls_src_x> TYPE x,
<ls_tgt_x> TYPE x.
"Add Client info the Table key if your table is Client dependent.
CONCATENATE sy-mandt lv_tabkey INTO lv_tabkey.
ASSIGN lv_tab_key TO <ls_src_x> CASTING.
ASSIGN ls_table TO <ls_tgt_x> CASTING.
<ls_tgt_x> = <ls_src_x>.
"Now ls_table has the key info filled including MANDT if you have the MANDT in table key.
SELECT *
FROM table_x
INTO TABLE DATA(lt_results)
WHERE key2 = ls_table-key2 AND key3 = ls_table-key3
AND key4 = ls_table-key4 AND key5 = ls_table_key5.
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.
With the below code I can retrieve the content of the internal table t_t005e, however when put into the field wa_upload-region, only the first column of the data is retrieved, however I want to retrieve the third column data.
TYPES: BEGIN OF ty_upload,
" ...
region TYPE regio,
" ...
END OF ty_upload.
DATA: wa_upload TYPE ty_upload,
t_t005e TYPE STANDARD TABLE OF t005e.
READ TABLE t_t005e
INTO wa_upload-region
WITH KEY land1 = 'GB'
regio = 'YK'
counc = ''.
As a result, I have created a work area wa_t005e, with the same type as the lines of t_t005e.
I want to first read the internal table t_t005e into the work area wa_t005e, then to the field wa_upload-region.
Following is my work in progress:
DATA: wa_t005e TYPE t005e.
LOOP AT t_t005e INTO wa_t005e.
ASSIGN COMPONENT wa_t005e-regio OF STRUCTURE
wa_t005e TO <wa_upload-region>.
ENDLOOP.
How to get the data of wa_t005e-regio into the field wa_upload-region?
There is no way of reading the value of only one column from a table directly into one field of a structure, at least in systems pre-7.40. If you do have a 7.40 system, you can use a "table expression" like this:
TRY.
wa_upload-region = t_t005e[ land1 = 'GB' regio = 'YK' counc = '' ]-regio.
CATCH cx_sy_itab_line_not_found.
ENDTRY.
In older system, you will have to read the whole table line into a structure, then you can just take the field from it, like this:
READ TABLE t_t005e INTO wa_t005e WITH KEY land1 = 'GB' regio = 'YK' counc = ''.
wa_upload-region = wa_t005e-regio.
If you want to use ASSIGN and the like, you can do that too. First you would read the table line into a structure again (in this case a field symbol to stay in theme). Then assign the needed component/field of the structure to a single-value field symbol.
DATA: t_upload TYPE STANDARD TABLE OF ty_upload,
t_t005e TYPE STANDARD TABLE OF t005e.
FIELD-SYMBOLS: <fs_upload> TYPE ty_upload,
<fs_t005e> TYPE t005e,
<region> TYPE regio. " or type any
SELECT *
FROM t005e
INTO CORRESPONDING FIELDS OF TABLE t_t005e.
READ TABLE t_t005e ASSIGNING <fs_t005e> WITH KEY land1 = 'GB' regio = 'YK' counc = ''.
ASSIGN COMPONENT 'REGIO' OF STRUCTURE <fs_t005e> TO <region>. " <---
*Other option: number of column
*ASSIGN COMPONENT 3 OF STRUCTURE <fs_t005e> TO <region>.
APPEND INITIAL LINE TO t_upload ASSIGNING <fs_upload>.
<fs_upload>-region = <region>.
WRITE <fs_upload>-region.
But is reading only one entry from the table really what you want to do? You didn't specify all keys of t005e in the READ statement. It would only select the first line that fits.
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.
I'm new in abap development .
Actually, I'm looking for the Number of fields of a table or structure
any suggestion please
For counting the components of a structure, better use RTTS as we don't want to access the database for result we already have in our environment.
DATA(lwa_struct) = VALUE kna1( ). " your structure e.g. kna1
DATA(lo_descr) = CAST cl_abap_structdescr( cl_abap_datadescr=>describe_by_data( lwa_struct ) ).
DATA(lw_all_fields) = LINES( lo_descr->components ). " => 184
#lausek answered correctly. But for release 730 or lower, you'll need to adapt to something like this:
data ls_mara type mara.
data lo_structdescr type ref to cl_abap_structdescr.
data lv_count type i.
lo_structdescr ?= cl_abap_datadescr=>describe_by_data( ls_mara ).
lv_count = lines( lo_structdescr->components ).
write lv_count.
Another solution from my side.
select count(*) into #data(lv_count) from dd03l
where tabname = 'YOUR STRUCTURE NAME' and AS4LOCAL = 'A'.