Get components of DDIC structure with includes - data-structures

I am using cl_abap_structdescr->get_components to get a list of fields in a structure. It works fine when I use it on locally declared structure types, but when I use it on DDIC structures, it doesn't give me the results I expect.
Reproducable example:
TYPES: BEGIN OF gty_outtab,
infty TYPE infty,
uname TYPE uname,
bdate TYPE datum,
btime TYPE uzeit,
pernr TYPE pernr_d,
opera TYPE hr_opera,
begda TYPE begda,
endda TYPE endda,
END OF gty_outtab.
DATA: lr_infty_structdescr TYPE REF TO cl_abap_structdescr,
lr_outtab_structdescr TYPE REF TO cl_abap_structdescr,
lt_outtab_components TYPE STANDARD TABLE OF abap_componentdescr,
lt_infty_components TYPE STANDARD TABLE OF abap_componentdescr.
" works as expected
lr_outtab_structdescr ?= cl_abap_structdescr=>describe_by_name( 'GTY_OUTTAB' ).
lt_outtab_components = lr_outtab_structdescr->get_components( ).
" doesn't work as expected
lr_infty_structdescr ?= cl_abap_structdescr=>describe_by_name( 'P0008' ).
lt_infty_components = lr_infty_structdescr->get_components( ).
BREAK-POINT.
Results:
That's okay for GTY_OUTTAB:
There are only two fields for P0008 although it contains many more fields (see below):
I already tried using cl_abap_typedescr instead and googled, but every code I find online looks just like mine?
Here is the definition of P0008 which contains many fields as you can see:

Of course after posting this, I found the reason why (german thread). Apparently, if the given structure contains included structures, then get_components won't break them up. Three solutions have been suggested in the thread and they all work great for me. Since I only need the structures' fieldnames, I will use option 1.
DATA: lt_infty_complist1 TYPE abap_compdescr_tab,
lt_infty_complist2 TYPE STANDARD TABLE OF fieldname,
lt_infty_complist3 TYPE abap_component_tab.
" 1 - get full fieldname list, but with barely any typedescription
lt_infty_complist1 = lr_infty_structdescr->components.
" 2 - get full fieldname list of DDIC structures, but without typedescription
SELECT fieldname
FROM dd03l
INTO TABLE lt_infty_complist2
WHERE tabname = 'P0008'.
DELETE lt_infty_complist2 WHERE table_line = '.INCLU--AP'
OR table_line = '.INCLUDE'.
" 3 - get full component list
" function code from: https://www.abapforum.com/forum/viewtopic.php?f=18&p=59840)
PERFORM return_components USING lr_infty_structdescr CHANGING lt_infty_complist3.

There is a method on cl_abap_structdescr called get_included_view( ) that will expand the included structures

Method GET_RTTS_FOR_LOCAL_TABLE of helper-class CL_CACS_RTTS_HELPER seems to do exactly what you want and what lacks your option 1
CALL METHOD cl_cacs_rtts_helper=>get_rtts_for_local_structure
EXPORTING
id_tabname = 'P0008'
receiving
ro_data = DATA(ro_struct)
.
It fetches all components of a structure into reference data object and also includes absolute types:

In the same class there is get field list method, which may be sufficient.
data: lo_incl_stru TYPE REF TO cl_abap_structdescr,
lt_field_list TYPE ddfields.
lt_field_list = lo_incl_stru->get_ddic_field_list( p_including_substructres = abap_true ).
If that isnt enough.... try
METHODS recursive_get_components
IMPORTING
io_structdescr TYPE REF TO cl_abap_structdescr
RETURNING
VALUE(rt_components) TYPE abap_component_tab.
METHOD recursive_get_components.
DATA:
lo_incl_stru TYPE REF TO cl_abap_structdescr,
lt_incl_comp TYPE abap_component_tab,
l_curr_index TYPE i.
FIELD-SYMBOLS: <comp> LIKE LINE OF rt_components,
<incl_comp> LIKE LINE OF lt_incl_comp.
rt_components = io_structdescr->get_components( ).
LOOP AT rt_components ASSIGNING <comp>.
IF <comp>-as_include = 'X'.
lo_incl_stru ?= <comp>-type. " not the include struc type
l_curr_index = sy-tabix. " and the index it is to be included
DELETE rt_components INDEX l_curr_index.
lt_incl_comp = recursive_get_components( io_structdescr = lo_incl_stru ).
LOOP AT lt_incl_comp ASSIGNING <incl_comp>.
INSERT <incl_comp> INTO rt_components INDEX l_curr_index.
l_curr_index = l_curr_index + 1.
ENDLOOP.
ENDIF.
ENDLOOP.
ENDMETHOD.

Related

Fetch value from XML using dynamic tag in ESQL

I have an xml
<family>
<child_one>ROY</child_one>
<child_two>VIC</child_two>
</family>
I want to fetch the value from the XML based on the dynamic tag in ESQL. I have tried like this
SET dynamicTag = 'child_'||num;
SET value = InputRoot.XMLNSC.parent.(XML.Element)dynamicTag;
Here num is the value received from the input it can be one or two. The result should be value = ROY if num is one and value is VIC if num is two.
The chapter ESQL field reference overview describes this use case:
Because the names of the fields appear in the ESQL program, they must be known when the program is written. This limitation can be avoided by using the alternative syntax that uses braces ( { ... } ).
So can change your code like this:
SET value = InputRoot.XMLNSC.parent.(XMLNSC.Element){dynamicTag};
Notice the change of the element type as well, see comment of #kimbert.

How can I compare strings case sensitive in xforms:select1 ref attribute?

I have to provide the ref attribute value in an xf:select1. I need to select names of properties only if they are present in the supportedProperties instance which can be done with the following:
<xf:select1
ref="
instance('properties')/property[
name = instance('supportedProperties')/property/name
]/name">
However, the problem is that supportedProperties can contain names which are in capital letters. Assuming we cannot change the instance, is there a way we can do a case sensitive comparison?
Tried to use the lower-case() XPath function as follows but it didn't work:
<xf:select1
ref="
instance('properties')/property[
name = instance('supportedProperties')/property/name
]/lower-case(name)">
Assuming you are using XPath 2, you can write:
<xf:select1
ref="
instance('properties')/property[
name = instance('supportedProperties')/property/name/lower-case(.)
]/name">
What this does is that the lower-case(.) function applies to all elements in the sequence returned by instance('supportedProperties')/property/name.
You can also write it:
<xf:select1
ref="
instance('properties')/property[
name = (
for $name in instance('supportedProperties')/property/name
return lower-case($name)
)
]/name">

What's the difference between 'Array<_>' & 'Array'

I'm getting this error when trying to build the app:
<unknown>:0: error: cannot assign value of type 'Array<_>' to type 'Array'
but Xcode is not indicating a specific line or class for the failure.
If I could understand the difference between
Array<_>
&
Array
it may help me locate the issue.
When you app is crashing, than you can turn on All Exceptions for Debug breakpoint. This should stop on the line where the crash appears.
You find this in Xcode on the Left Panel-> BreakPoint Navigator.
Than press the + in the bottome left corner and Add Exception Breakpoint.
It looks like that you overwrite an array with an array that has a specific value definition. Good Luck :)
A generic argument clause is enclosed in angle brackets ( < > )
< generic argument list >
You can replace a type parameter with a type argument that is itself a specialized version of a generic type.For example, you can replace the type parameter T in Array < T > with a specialized version of an array, Array< Int > , Array< String >, to form an array whose elements are themselves array of integers / Strings
Regarding xour code Snippet the fix is:
u should define it with the right value ... userTweetsArray : [String] = String . or remove the : [String] because setting the value defines already the object and type :)
I don't like answering my own questions but for the sake of closure
I had this line of code
var userTweetsArray : Array = [String]()
I never actually used it. Once I removed that line the error had gone.
The error was caused by assigning an array of a type in this case String to an Array of no type.
So difference is Array<_> is an array of a type and Array is not.

How to WRITE a structure?

How can I do the following:
data: ls_header type BAPIMEPOHEADER.
" fill it
write ls_header.
currently I'm getting an error because write can not parse the complex type to a string. Is there a simple way to get this code running in abap?
You could use something like:
DATA: g_struct TYPE bapimepoheader.
DO.
ASSIGN COMPONENT sy-index OF STRUCTURE g_struct TO FIELD-SYMBOL(<f>).
IF sy-subrc NE 0.
EXIT.
ENDIF.
WRITE: / <f>.
ENDDO.
Perhaps not exactly the answer you expect: If you list each field.
This can be done quite easy via the Pattern-mask in SE38:
Select the Write-pattern:
Enter the structure you want:
Select the fields
Confirm with "Copy"
Confirm and you get
WRITE: bapimepoheader-po_number,
bapimepoheader-comp_code,
bapimepoheader-doc_type,
bapimepoheader-delete_ind,
bapimepoheader-status,
bapimepoheader-creat_date,
bapimepoheader-created_by,
bapimepoheader-item_intvl,
bapimepoheader-vendor,
bapimepoheader-langu,
bapimepoheader-langu_iso,
bapimepoheader-pmnttrms,
bapimepoheader-dscnt1_to,
bapimepoheader-dscnt2_to,
bapimepoheader-dscnt3_to,
bapimepoheader-dsct_pct1,
bapimepoheader-dsct_pct2,
bapimepoheader-purch_org,
bapimepoheader-pur_group,
bapimepoheader-currency,
bapimepoheader-currency_iso,
bapimepoheader-exch_rate,
bapimepoheader-ex_rate_fx,
bapimepoheader-doc_date,
bapimepoheader-vper_start,
bapimepoheader-vper_end,
bapimepoheader-warranty,
bapimepoheader-quotation,
bapimepoheader-quot_date,
bapimepoheader-ref_1,
bapimepoheader-sales_pers,
bapimepoheader-telephone,
bapimepoheader-suppl_vend,
bapimepoheader-customer,
bapimepoheader-agreement,
bapimepoheader-gr_message,
bapimepoheader-suppl_plnt,
bapimepoheader-incoterms1,
bapimepoheader-incoterms2,
bapimepoheader-collect_no,
bapimepoheader-diff_inv,
bapimepoheader-our_ref,
bapimepoheader-logsystem,
bapimepoheader-subitemint,
bapimepoheader-po_rel_ind,
bapimepoheader-rel_status,
bapimepoheader-vat_cntry,
bapimepoheader-vat_cntry_iso,
bapimepoheader-reason_cancel,
bapimepoheader-reason_code,
bapimepoheader-retention_type,
bapimepoheader-retention_percentage,
bapimepoheader-downpay_type,
bapimepoheader-downpay_amount,
bapimepoheader-downpay_percent,
bapimepoheader-downpay_duedate,
bapimepoheader-memory,
bapimepoheader-memorytype,
bapimepoheader-shiptype,
bapimepoheader-handoverloc,
bapimepoheader-shipcond,
bapimepoheader-incotermsv,
bapimepoheader-incoterms2l,
bapimepoheader-incoterms3l.
Now you can make a simple replacement of bapimepoheader with ls_header and you have an output of all fields of the structure.
Maybe this is not elegant and you must adapt your report, if the structure changes. But I like this way, because often I don't need all fields and I can select the fields in an easy way.
I know two ways, one is procedural, the other is oop.
Here is the procedural approach.
Select the structure's fields (or whatever else You might need ) from the data-dictionary table DD03L into a local internal table.
Loop over the table into a work-area
Check, whether current field is a flat single datatype, and if so,
Assign component workarea-fieldname of structure ls_header into anyfieldsymbol
Write anyfieldsymbol
Do You need the code ?
Class CL_ABAP_CONTAINER_UTILITIES was specially introduced for that by SAP.
Use FILL_CONTAINER_C method for output the structure in a WRITE manner:
DATA: ls_header type BAPIMEPOHEADER.
CALL METHOD CL_ABAP_CONTAINER_UTILITIES=>FILL_CONTAINER_C
EXPORTING
IM_VALUE = ls_header
IMPORTING
EX_CONTAINER = DATA(container)
EXCEPTIONS
ILLEGAL_PARAMETER_TYPE = 1
others = 2.
WRITE container.
You can write your structure to a string and then output the string. Same method idoc segments are created.

Passing Field Symbol value to Return parameter of method

I have the below code which uses a method. When I try to assign the Field Symbol value [Type ANY] to the return parameter RO_TAB [Type Ref to Data], I am getting an error message OBJECTS_MOVE_NOT SUPPORTED [Conversion of type "l" to type "g" not supported.].
The issue is happening after a BW system upgrade along with which we also moved to ABAP objects. The code executes perfectly in the older version of ABAP.
The dump occurs in the below line:
RO_TAB = <lf_storage>.
I have no idea why.
method GET_LU_STORAGE_FOR_ODS.
* IMPORTS
* IF_ODS TYPE RSODSTECH
* IF_ODS_TABLE_TYPE TYPE ZODS_TAB_TYPE
* RETURNS
* RO_TAB TYPE REF TO DATA
FIELD-SYMBOLS:
<lf_storage> TYPE ANY.
DATA:
lf_index TYPE SY-TABIX,
lf_sindex TYPE STRING,
lf_name TYPE STRING.
lf_index = GET_LU_STORAGE_INDEX(
IF_ODS = IF_ODS
IF_ODS_TABLE_TYPE = IF_ODS_TABLE_TYPE ).
lf_sindex = lf_index.
CONCATENATE
'MO_LU_DATA_'
lf_sindex
INTO lf_name.
ASSIGN lf_name TO <lf_storage>.
RO_TAB = <lf_storage>.
endmethod.
You need to create a data object first, using the CREATE DATA statement. Then you can ASSIGN a field symbol to work with the dynamically created data object. There's an example in the online manual. A field symbol is not a reference, it simply places the variable assigned to it in its position. You're effectively trying to move a string (which is what lf_name is) to a reference variable, and that won't work.
You cannot assign a variable of type STRING to a variable of type REF TO DATA.
The following code snippet shows how it should be done.
DATA: lf_name TYPE string.
DATA: lo_tab TYPE REF TO DATA.
FIELD-SYMBOLS: <lf_name> TYPE string.
lf_name = 'test'.
GET REFERENCE OF lf_name INTO lo_tab.
*lf_name = lo_tab. "this is not allowed
ASSIGN lo_tab->* TO <lf_name>.
So in your case it would be sufficient to define a field symbol.
FIELD-SYMBOLS: <lf_name> TYPE STRING.
then assign the contents referenced by RO_TAB to this field symbol.
ASSIGN ro_tab->* TO <lf_name>.
and finally do the concatenation.
CONCATENATE
'MO_LU_DATA_'
lf_index
INTO <lf_name>.
That's all! No further assignments should be required.
How about just this?
lf_sindex = lf_index.
CONCATENATE
'MO_LU_DATA_'
lf_sindex
INTO RO_TAB.

Resources