This is a test message to test the length of the message box.
Login
|
ABAP Polymorphic Structures
Created by Software-Heroes

ABAP - Polymorphic Structures

239

What about structures in dynamic ABAP development? Processing has been rather generic up to now. With polymorphic structures, development can now gain more structure.

Advertising


In this article, we'll take a look at the new polymorphic structures. What can you already do with them, and what can't you do yet? More details in the article.

 

Introduction

Dynamic or generic development is the most flexible development method you can use, but it also means that you generate a lot of source code to process a small amount of data or provide generic code components. This is mostly due to the fact that more checks, variables, and assignments are often needed because you can't access them directly via the component name. For example, mapping structures and ultimately accessing the various components of those structures is a very common and extensive topic. SAP has provided an initial solution for this using polymorphic structures, which are intended to reduce some of the generic code, thus enabling simpler and shorter ABAP source code. Polymorphic structures were introduced with Release 2608 in the ABAP Environment and in the public cloud and will likely be rolled out to on-premises and private cloud environments with Release 2027.

 

Preparation

First, we need some structures, tables, and data as preparation. To do this, we will create two data types that will be reused in all structures. Additionally, we create three structures: one for the supplier and the customer, each containing the identifier and name, and one structure omitting both fields to allow for later testing of mapping settings.

TYPES identifier TYPE c LENGTH 12.
TYPES name       TYPE string.

TYPES:
  BEGIN OF none_field,
    some_id  TYPE c LENGTH 10,
    location TYPE string,
    value    TYPE int8,
  END OF none_field.
TYPES none_fields TYPE STANDARD TABLE OF none_field WITH EMPTY KEY.

TYPES:
  BEGIN OF customer,
    customer_id TYPE c LENGTH 10,
    location    TYPE string,
    identifier  TYPE identifier,
    name        TYPE name,
    value       TYPE int8,
  END OF customer.
TYPES customers TYPE STANDARD TABLE OF customer WITH EMPTY KEY.

TYPES:
  BEGIN OF supplier,
    supplier_id    TYPE c LENGTH 10,
    contract_value TYPE p LENGTH 16 DECIMALS 2,
    value          TYPE int8,
    identifier     TYPE identifier,
    name           TYPE name,
  END OF supplier.
TYPES suppliers      TYPE STANDARD TABLE OF supplier WITH EMPTY KEY.

TYPES generic_input  TYPE ANY STRUCTURE CONTAINING identifier TYPE identifier
                                                   name       TYPE name.
TYPES generic_inputs TYPE STANDARD TABLE OF generic_input WITH EMPTY KEY.

 

To have data right away, we'll create two data sets in tabular form for both the Supplier and the Customer, and also for the missing objects. Finally, we'll read the first row from each table to gain experience working with tables and then with structures.

FINAL(customers) = VALUE customers( ( customer_id = 'C0001'
                                      location    = `Teststreet 1, 51100 Cologne`
                                      identifier  = '10'
                                      name        = 'John Doe'
                                      value       = 98 )
                                    ( customer_id = 'C0002'
                                      location    = `Test Allee 3, 21230 Berlin`
                                      identifier  = '20'
                                      name        = 'Jane Doe'
                                      value       = 13 ) ).

FINAL(suppliers) = VALUE suppliers( ( supplier_id    = 'S0001'
                                      contract_value = '10000.00'
                                      identifier     = '20'
                                      name           = 'Jane Doe'
                                      value          = 17 )
                                    ( supplier_id    = 'S0002'
                                      contract_value = '15000.00'
                                      identifier     = '10'
                                      name           = 'John Doe'
                                      value          = 23 ) ).

FINAL(nones) = VALUE none_fields( ( some_id  = 'N0001'
                                    location = `Teststreet 1, 51100 Cologne`
                                    value    = 40 )
                                  ( some_id  = 'N0002'
                                    location = `Test Allee 3, 21230 Berlin`
                                    value    = 16 ) ).

FINAL(customer) = customers[ 1 ].
FINAL(supplier) = suppliers[ 1 ].
FINAL(none) = nones[ 1 ].

 

Definition

Let's first look at the definition of the data type and how we can create and use it.

 

Creation

Basically, we would expect to define a structure. That means we define a new type with a name and give it the type ANY STRUCTURE. With this, we have defined a generic structure. Finally, we define, using the CONTAINING addition, which fields must be present in the structure at a minimum. The fields then have a name and a type that is defined. The compiler then checks at runtime whether a corresponding element with that name exists and whether the type, if it is not a 100% match, can at least be cast to the target type.

TYPES generic_input  TYPE ANY STRUCTURE CONTAINING 
    identifier TYPE identifier
    name       TYPE name.

 

If we can create a structure, we can of course also define a table. Here we use the standard by specifying the name of the type, for example using STANDARD TABLE or SORTED TABLE as the type, and using a key. This defines a table type that then inherits from our generic structure type, which means we can then, for example, pass tables and not just individual data records to a method.

TYPES generic_inputs TYPE STANDARD TABLE OF generic_input WITH EMPTY KEY.

 

Interfaces

We can then use this data type to define a parameter within a method interface. There are no deviations from the standard here; you work with the type normally.

METHODS extract_name
  IMPORTING !out   TYPE REF TO if_oo_adt_classrun_out
            !input TYPE generic_input.

 

Variables

However, things look a little different if you want to perform an inline declaration or type assignment of a local element. Basically, we can write the addition and use the type. However, we will then receive an error message directly from the compiler.

DATA local TYPE generic_input.

 

These data types are not intended for direct declaration, but should primarily be used for interfaces, parameters, and field symbols. This is also shown by the error message we receive, which prevents us from activating our ABAP code.

 

Assignment

Let's now look at various examples of how we can assign and use data.

 

Methods

Above, we defined various methods for receiving data and processing it via a generic structure. Therefore, we simply need to call the method here and pass the dynamic type as an input parameter, in this case Customer and Supplier. These different types do not generate any error messages and are accepted one-to-one. This works for structures and tables.

extract_name( out   = out
              input = customer ).
extract_name( out   = out
              input = supplier ).

 

However, things look a bit different when we use the type NONE: Since this doesn't contain a field defined in the CONTAINING addition, we already get an error message from the compiler. This is because we have a typed data type, and the system recognizes that it doesn't have a corresponding minimum field.

 

Loops

Let's look at the processing of tables within the methods. For this, we go to the EXTRACT_NAMES method, where we pass an entire table generically. Here, it's not so easy to execute an inline declaration directly in the LOOP. We would get an error message here because we are working with a generic type. If we want to use a reference, we must first define it with REF TO DATA.

DATA input TYPE REF TO data.

LOOP AT inputs REFERENCE INTO input.
  extract_name( out   = out
                input = input->* ).
ENDLOOP.

 

Field symbols are handled slightly differently: Here, we don't need to define a field symbol with TYPE ANY first, but can work directly with an inline declaration and pass the field symbol to our method.

LOOP AT inputs ASSIGNING FIELD-SYMBOL(<input>).
  extract_name( out   = out
                input = <input> ).
ENDLOOP.

 

Processing

We've looked at the definition and the passing of data. Now we'll move on to the actual processing and how much code we can save.

 

Current

So what does it look like if we normally want to access two attributes generically, in this case the identifier and the name, in order to extract or output them during processing? For this, we need to execute an ASSIGN COMPONENT, specify the name of the component, and assign it to a field symbol. At the same time, however, we also need to check whether the assignment was successful or whether this field doesn't even exist in the structure. Finally, we can then generate the output for the two pieces of information.

ASSIGN COMPONENT 'IDENTIFIER' OF STRUCTURE input TO FIELD-SYMBOL(<identifier>).
IF sy-subrc <> 0.
  RETURN.
ENDIF.

ASSIGN COMPONENT 'NAME' OF STRUCTURE input TO FIELD-SYMBOL(<name>).
IF sy-subrc <> 0.
  RETURN.
ENDIF.

out->write( |{ <identifier> } - { <name> }| ).

 

New

Now let's look at the processing with the new structures. We can see immediately that we no longer need all the overhead of dynamic development. Because we have defined that the identifier and the name must be present in the structure, we can work directly with them in the method and access these two field names. No check for their existence is necessary.

out->write( |{ input-identifier } - { input-name }| ).

 

We also receive direct suggestions from the autocomplete function, which makes our work in development easier again, without having to guess existing fields.

 

Complete Example

In this chapter you will find the complete executable class to recreate the example in your system. However, you will also need the appropriate ABAP release.

CLASS zcl_bs_demo_poly_structures DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PRIVATE SECTION.
    TYPES identifier TYPE c LENGTH 12.
    TYPES name       TYPE string.

    TYPES:
      BEGIN OF none_field,
        some_id  TYPE c LENGTH 10,
        location TYPE string,
        value    TYPE int8,
      END OF none_field.
    TYPES none_fields TYPE STANDARD TABLE OF none_field WITH EMPTY KEY.

    TYPES:
      BEGIN OF customer,
        customer_id TYPE c LENGTH 10,
        location    TYPE string,
        identifier  TYPE identifier,
        name        TYPE name,
        value       TYPE int8,
      END OF customer.
    TYPES customers TYPE STANDARD TABLE OF customer WITH EMPTY KEY.

    TYPES:
      BEGIN OF supplier,
        supplier_id    TYPE c LENGTH 10,
        contract_value TYPE p LENGTH 16 DECIMALS 2,
        value          TYPE int8,
        identifier     TYPE identifier,
        name           TYPE name,
      END OF supplier.
    TYPES suppliers      TYPE STANDARD TABLE OF supplier WITH EMPTY KEY.

    TYPES generic_input  TYPE ANY STRUCTURE CONTAINING identifier TYPE identifier
                                                       name       TYPE name.
    TYPES generic_inputs TYPE STANDARD TABLE OF generic_input WITH EMPTY KEY.

    METHODS extract_name_generic
      IMPORTING !out   TYPE REF TO if_oo_adt_classrun_out
                !input TYPE any.

    METHODS extract_name
      IMPORTING !out   TYPE REF TO if_oo_adt_classrun_out
                !input TYPE generic_input.

    METHODS extract_names
      IMPORTING !out   TYPE REF TO if_oo_adt_classrun_out
                inputs TYPE generic_inputs.
ENDCLASS.


CLASS zcl_bs_demo_poly_structures IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    FINAL(customers) = VALUE customers( ( customer_id = 'C0001'
                                          location    = `Teststreet 1, 51100 Cologne`
                                          identifier  = '10'
                                          name        = 'John Doe'
                                          value       = 98 )
                                        ( customer_id = 'C0002'
                                          location    = `Test Allee 3, 21230 Berlin`
                                          identifier  = '20'
                                          name        = 'Jane Doe'
                                          value       = 13 ) ).

    FINAL(suppliers) = VALUE suppliers( ( supplier_id    = 'S0001'
                                          contract_value = '10000.00'
                                          identifier     = '20'
                                          name           = 'Jane Doe'
                                          value          = 17 )
                                        ( supplier_id    = 'S0002'
                                          contract_value = '15000.00'
                                          identifier     = '10'
                                          name           = 'John Doe'
                                          value          = 23 ) ).

    FINAL(nones) = VALUE none_fields( ( some_id  = 'N0001'
                                        location = `Teststreet 1, 51100 Cologne`
                                        value    = 40 )
                                      ( some_id  = 'N0002'
                                        location = `Test Allee 3, 21230 Berlin`
                                        value    = 16 ) ).

    FINAL(customer) = customers[ 1 ].
    FINAL(supplier) = suppliers[ 1 ].
    FINAL(none) = nones[ 1 ].

    out->write( `Extract Generic:` ).
    extract_name_generic( out   = out
                          input = customer ).
    extract_name_generic( out   = out
                          input = supplier ).
    extract_name_generic( out   = out
                          input = none ).

    out->write( `Extract Name:` ).
    extract_name( out   = out
                  input = customer ).
    extract_name( out   = out
                  input = supplier ).
*    extract_name( out   = out
*                  input = none ).

    out->write( `Extract Names:` ).
    extract_names( out    = out
                   inputs = customers ).
    extract_names( out    = out
                   inputs = suppliers ).
*    extract_names( out    = out
*                   inputs = nones ).
  ENDMETHOD.


  METHOD extract_name_generic.
    ASSIGN COMPONENT 'IDENTIFIER' OF STRUCTURE input TO FIELD-SYMBOL(<identifier>).
    IF sy-subrc <> 0.
      RETURN.
    ENDIF.

    ASSIGN COMPONENT 'NAME' OF STRUCTURE input TO FIELD-SYMBOL(<name>).
    IF sy-subrc <> 0.
      RETURN.
    ENDIF.

    out->write( |{ <identifier> } - { <name> }| ).
  ENDMETHOD.


  METHOD extract_name.
    out->write( |{ input-identifier } - { input-name }| ).
  ENDMETHOD.


  METHOD extract_names.
    DATA input TYPE REF TO data.

    LOOP AT inputs REFERENCE INTO input.
      extract_name( out   = out
                    input = input->* ).
    ENDLOOP.

    LOOP AT inputs ASSIGNING FIELD-SYMBOL(<input>).
      extract_name( out   = out
                    input = <input> ).
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

 

Conclusion

Dynamic programming and accessing fields within structures can be facilitated if we actually use polymorphic structures. However, minor obstacles may still arise with typing or passing data, as well as if we want to work completely dynamically and receive no information from the compiler. However, they generally offer real added value for development.

 

Further information:
SAP Help - ANY STRUCTURE CONTAINING
SAP Help - Demo Program


Included topics:
New ABAPPolymorphicStructureCONTAINING
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Tuesday and Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP - XCO Logging

Category - ABAP

The XCO classes are part of the ABAP Cloud APIs and offer numerous functions that aren't always easy to understand. In this article, we'll take a detailed look at the logging object.

12/16/2025

ABAP - The right Key

Category - ABAP

What about the use of internal tables? Is it still just TYPE TABLE in ABAP, and the table is fully defined?

11/14/2025

ABAP - XCO Regular Expressions

Category - ABAP

Let's take a look at the XCO classes for regular expressions and how you can easily use them to execute REGEX against text and input in ABAP Cloud. We'll also compare them with classic ABAP.

11/07/2025

ABAP - Escape

Category - ABAP

In this article, let's take a closer look at different escape variants that you need for ABAP development and system security.

10/07/2025

ABAP - Date and Time

Category - ABAP

In this article, let's take a closer look at the data types for dates and times in ABAP. Have any changes been made between the various releases, and what should you still use today?

10/03/2025