Skip to Content

Dynamically Handling Structure Data Types Defined on a Remote Dictionary

Suppose you’re calling a remote enabled function module and its signature parameter’s data types don’t exist in local data dictionary (for example because of different sap product such as SRM and R/3 for example). Thinking about a structure data type probably you don’t have defined in your calling system data dictionary even the most of data elements used for defining each field. In order to pass importing parameters and receive result you must replicate signature data types. So, usually, data types are simply replicated on calling system; coding them (for example in a type group) or defining them in local data dictionary. For deeper structures with tens of fields could be a long and annoying job. But what if a structure data type defined on remote system, maybe because of an upgrade or because of a custom include, changes ? That generates a field displacement between local and remote structures that could even lead to a dump (or simply to a field misread). In that case you must realign local data types with remote ones. So, how to avoid definitely field displacement and dumps ? The only way to completely avoid any intervention is to dynamically build structure data types at runtime respecting remote definition.   Runtime generation of structures (and internal table) data types based on   structure data types defined in local dictionary is quite easy by means of cl_abap_structdescr and cl_abap_tabledescr classes. Method describe_by_name of class cl_abap_structdescr allows us to know “building details” of a   specific structure data type.

A quick example using structure BAPIRET2

* data declarations  

  data: lo_struct type ref to cl_abap_structdescr, 
        lt_comp   type cl_abap_structdescr=>component_table, 
        lo_run_st type ref to cl_abap_structdescr, 
        lo_run_tt type ref to cl_abap_tabledescr, 
        lo_struc  type ref to data, 
        lo_ttype  type ref to data.

  field-symbols: <fs_table> type standard table, 
                 <fs_line>  type any.

* describe local structure  
  lo_struct ?= cl_abap_structdescr=>describe_by_name( 'BAPIRET2' ).

* get local structure components  
  lt_comp = lo_struct->get_components( ).

* create structure and table type descriptor  
  lo_run_st = cl_abap_structdescr=>create( lt_comp ).   
  lo_run_tt = cl_abap_tabledescr=>create( p_line_type  = lo_struct  
                                          p_table_kind = cl_abap_tabledescr=>tablekind_std  
                                          p_unique     = abap_false ).

* handle runtime data type by means of generic variables  
  create data: lo_struc type handle lo_run_st,
               lo_ttype type handle lo_run_tt.   

  assign: lo_struc->* to <fs_line>,
          lo_ttype->* to <fs_table>.

By means of standard function module ‘DDIF_FIELDINFO_GET’ (defined as remote   enabled and for sure present in both sap system) is possible to know building   details (field by field) of a specific structure data type. So, instead of   building our structure data type using details returned by the static method describe_by_name of cl_abap_structdescr  class we  could build it using abap predefined data types (because data element may not exist locally) by means of what ‘DDIF_FIELDINFO_GET’ returns in table parameter “dfies_tab”.

That snippet (the code of my zcl_dyn_remote_type_builder=>get_elemdescr method) shows how to build structure components (so a descriptor, an instance of cl_abap_elemdescr) starting from knowledge of internal length, internal type and decimal places. That code was recently updated (thanks to my colleague Fabrizio Gemma) for solving an INT1 and INT2 bug.

  data: lx_pir  type ref to cx_parameter_invalid_range.

  data: lv_int2 type int2,
        lv_int1 type int1. 


      case i_inttype.

        when cl_abap_elemdescr=>typekind_int.
          result = cl_abap_elemdescr=>get_i( ).

        when cl_abap_elemdescr=>typekind_int1.
          result ?= cl_abap_elemdescr=>describe_by_data( p_data = lv_int1 ).

        when cl_abap_elemdescr=>typekind_int2.
          result ?= cl_abap_elemdescr=>describe_by_data( p_data = lv_int2 ).

        when cl_abap_elemdescr=>typekind_float.
          result = cl_abap_elemdescr=>get_f( ).

        when cl_abap_elemdescr=>typekind_date.
          result = cl_abap_elemdescr=>get_d( ).
        when cl_abap_elemdescr=>typekind_packed.
          result = cl_abap_elemdescr=>get_p( p_length   = i_intlen
                                             p_decimals = i_decimals ).

        when cl_abap_elemdescr=>typekind_char.
          result = cl_abap_elemdescr=>get_c( p_length = i_intlen ).

        when cl_abap_elemdescr=>typekind_time.
          result = cl_abap_elemdescr=>get_t( ).

        when cl_abap_elemdescr=>typekind_num.
          result = cl_abap_elemdescr=>get_n( p_length = i_intlen ).

        when cl_abap_elemdescr=>typekind_hex.
          result = cl_abap_elemdescr=>get_x( p_length = i_intlen ).

        when cl_abap_elemdescr=>typekind_string.
          result = cl_abap_elemdescr=>get_string( ).

        when cl_abap_elemdescr=>typekind_xstring.
          result = cl_abap_elemdescr=>get_xstring( ).

        when others.

          raise exception type zcx_dyn_remote_type_builder
              textid  = zcx_dyn_remote_type_builder=>no_inttype
              inttype = i_inttype.


    catch cx_parameter_invalid_range into lx_pir.
      raise exception lx_pir.


and here is how to use it (fixing also a unicode bug found in previous version of code):

* data declaration

  data: lo_elem      type ref to cl_abap_elemdescr.

  data: lt_fields    type standard table of dfies,
        ls_comp      type abap_componentdescr,
        ls_dfies     type dfies,
        ls_tmp_dfies type dfies,
        ls_x030l     type x030l,
        lv_intlen    type i,
        lv_decimals  type i,
        lv_off       type i,
        lv_count     type numc3.

  data: lx_parameter_invalid_range type ref to cx_parameter_invalid_range.

* rfc destination check
  if not i_rfcdest is initial.

    call function 'RFC_CHECK_DESTINATION'
        mydest                        = i_rfcdest
        mytype                        = zcl_dyn_remote_type_builder=>rfctype3
        empty_destination             = 1
        invalid_logical_destination   = 2
        destination_with_special_char = 3
        internal_destination_id       = 4
        empty_rfctype                 = 5
        others                        = 6.

    if sy-subrc ne 0.

      raise exception type zcx_dyn_remote_type_builder
          textid  = zcx_dyn_remote_type_builder=>rfc_unreachable         
          rfcdest = i_rfcdest.



* get dictionary information from remote system
  call function 'DDIF_FIELDINFO_GET' destination i_rfcdest
      tabname        = i_struct
      x030l_wa       = ls_x030l
      dfies_tab      = lt_fields
      not_found      = 1
      internal_error = 2
      others         = 3.

  if sy-subrc ne 0.

    raise exception type zcx_dyn_remote_type_builder
        textid  = zcx_dyn_remote_type_builder=>no_struc
        struct  = i_struct.


* component table builder 

*     build structure field by field  
      loop at lt_fields into ls_dfies. 

        case ls_dfies-inttype. 

          when cl_abap_elemdescr=>typekind_char or 
               cl_abap_elemdescr=>typekind_date or 
               cl_abap_elemdescr=>typekind_time or

            lv_intlen  = ls_dfies-intlen / ls_x030l-unicodelg. 

          when others. 

            lv_intlen  = ls_dfies-intlen. 


        lv_decimals = ls_dfies-decimals.

*       offset management with a dummy filed (if needed)  
        lv_off = ls_dfies-offset - ( ls_tmp_dfies-offset + ls_tmp_dfies-intlen ).

        if lv_off gt 0.

          lo_elem = cl_abap_elemdescr=>get_x( lv_off ).
          ls_comp-type = lo_elem.
          add 1 to lv_count.

          concatenate zcl_dyn_remote_type_builder=>offset '_' lv_count into ls_comp-name.       

          append ls_comp to result.

          free: lo_elem.


*       field management by means of internal abap types  
        ls_comp-name = ls_dfies-fieldname.

*       build element  
        lo_elem = zcl_dyn_remote_type_builder=>get_elemdescr( i_inttype  = ls_dfies-inttype  
                                                              i_intlen   = lv_intlen
                                                              i_decimals = lv_decimals ).

*       assign element
        ls_comp-type = lo_elem.

        append ls_comp to result.

        ls_tmp_dfies = ls_dfies.

        clear: ls_dfies.


    catch cx_parameter_invalid_range into lx_parameter_invalid_range. 

      raise exception lx_parameter_invalid_range.  


In this nugg you could see a concrete example of how it works.

I would like to special thank my colleague Fabrizio Gemma for discovering and solving a bug involving datatype INT1 and INT2 on method GET_ELEM_DESCR of ZCL_DYN_REMOTE_TYPE_BUILDER class and Hans Pettersson for discovering and solving a bug concerning substructure includes. The code shown two boxes above has been updated according to their suggestion: since there isn’t any GET_* method in CL_ABAP_ELEMDESCR class for datatype INT1 and INT2 Fabrizio solved applying the DESCRIBE_BY_DATA method to declared variables of these types and casting the result; Hans solved the problem reviewing offset calculation logic and dummy field generation.

Starting from this point i extended remote runtime data-typing to handle also nested structures and nested internal tables (the nugg already includes these changes). For more details i refer you to my new blog.

The nugg contains:

  • class ZCL_DYN_REMOTE_TYPE_BUILDER whose static methods + CREATE_STRUCT_TYPE+ and CREATE_TABLE_TYPE build structype and tabletype object that a variable could handle at runtime.
  • exception class ZCX_DYN_REMOTE_TYPE_BUILDER (and message classZDYNTYPEBUILDER)
  • program ZDYNSTRUCTBUILDERDEMO that shows an example remote calling function module “BAPI_USER_GET_DETAIL” (supposing result structures aren’t available locally)

Recently i spent a little bit of my free time to modernize my code, reviewing and rewriting it in a full object oriented way. I also improved it with additional features for both remote typing and querying.

The new nugg can be downloaded there. There’s a program named zsrqldemo showing some code examples.

You must be Logged on to comment or reply to a post.
  • Thanks you for sharing your great idea.
    This way avoids replication of the FM interface's structures in the client system.
    The benefits are obvious, especially after a release upgrade.
    • Nice, but the Unicode alignment logic doesn't hold (unless all components are clike). There are two flaws, as I see it:

      1. In the calculation of the gap length (lv_off), both offsets (may be built up of both clike and non-clike components) are divided by the Unicode character representation, but the length of the preceding component (lv_prelen) is divided by the Unicode character representation only for clike.

      This can result in situations like the following, assuming unicodelg = 2:


      A         X                        1              0

      B         C                        8              2

      C         X                        8            10

      D         I                         4             20

      Here, the alignment gap between A and B is 1 byte, but it won't be filled by a dummy component since it is calculated as:

         lv_off = 2 / 2 - ( 0 / 2 + 1 ) = 0

      The gap between C and D is 2 bytes but is calculated as:

        lv_off = 20 / 2 - ( 10 / 2 + 8 ) = -3

      2. The gaps are assumed to be full character gaps (dummy components are created with cl_abap_elemdescr=>get_c). Single-byte gaps like in the first example above cannot be filled.

      My suggestion would be to simply calculate a possible gap as:

        lv_off =     ls_dfies-offset -   ( ls_tmp_dfies-offset + ls_tmp_dfies-intlen ).

      and to create any dummy components with cl_abap_elemdescr=>get_x( lv_off ).

      • Hi Hans,

        it was just a meditation looking at the code or did you notice a bug, a field misplacement or anything else?

        Ask this because i tested the suggested case from a Unicode System to both Unicode/Non Unicode RFC connected systems and i didn't get any issue. In Unicode-Unicode RFC connection my dynamically typed line is exactly as dictionary defined. In Unicode-Non Unicode RFC connection my dynamically typed line has an additional field named OFFSET_01 between field 3 and field 4 but no side-effects: moving corresponding fields from a valued structure typed as dictionary and my dynamically typed line everything's fine.

        • Hi Manuel,

          Actually found it as a bug. I had developed something similar to your solution but got alignment problems when calling a Unicode system. I then found your article and tested its filling logic, which took care of most, but not all, of the gaps (four out of six in my case). That's when I started to analyse it in more depth and drew the conclusions above. I switched to the logic I indicated, and that solved the problem.

          • Hi Hans,

            i explored and tested my code much more in depth (in fact it is quite rare to exchange x data type) but sincerely i didn't notice any issues. What i did is to test an rfc call to a module exporting a valued line with 4 fields: F1[RAW(1)], F2[CHAR(4)], F3[RAW(8)], F4[INT4(10)] and assigning the result to a line dynamically built [based on dictionary on same rfc destination]. I tested all 4 cases [Unicode->NonUnicode, Unicode->Unicode, NonUnicode->Unicode and NonUnicode->NonUnicode] and i didn't notice any field misplacement or truncation. Everything seems to be fine. In Unicode->NonUnicode and Non Unicode->NonUnicode call there's a generated offset field between F3 and F4 but field mapping is fine; the offset field remains empty when returning from rfc call.

            You downloaded my github nugg or, since you developed something similar, you adapted your developments based on my filing logic?

            Anyway, if you want to go deeper into the problem we can share much more technical details by email.

          • Hi Hans, thanks for sharing with me your scenario. I did some tests and you're right. The issue is due to the presence of structure includes; as far as i tested your fix seems ok for both Unicode/Non Unicode communication. So i updated my nugg (and my blog). Thank you very much.