ABAP Deep Dive - CORRESPONDING
In diesem Artikel einmal etwas mehr über das neue Corresponding Statement und wie man es im Detail einsetzen kann. Dabei schauen wir einmal auf die zusätzlichen Features.
Inhaltsverzeichnis
Bereits in einem älteren Artikel sind wir bereits auf das neue Statement eingegangen und haben einige Vorzüge davon erklärt. In diesem Artikel wollen wir uns einige neue Faceten anschauen und wie du sie optimal nutzen kannst. Uns ist auch aufgefallen, dass durch Entwickler meist nicht das volle Potential der Statements genutzt wird, um Coding einzusparen. In diesem Zusammenhang wollen wir uns einige Neuerungen und Besonderheiten anschauen.
Einleitung
Der neue CORRESPONDING ist ein Konstruktor-Ausdruck, dass heißt er erzeugt ein neues Element aus seinem Quellobjekt. Hierbei kannst du es dir so vorstellen, dass dazu ein neues Element fürs Ziel erzeugt wird, dabei leer ist und dann die Zuweisung nach den entsprechenden Regeln durchgeführt wird. Die Werte werden dabei kopiert und das Quellobjekt nicht verändert. Dies hat den schönen Vorteil, dass du dir zum Beispiel innerhalb eines Loops das Clear sparst, wenn die Loop-Struktur einer neuen Struktur für die Verarbeitung zugewiesen wird.
Vorbereitung
Für die nachfolgenden Beispiele definieren wir uns separate Strukturen und Tabellentypen, die wir nutzen möchten. Dabei gibt es immer eine Quellstruktur und eine Zielstruktur, bei der einige Feldnamen gleich sind und einige unterschiedlich. Im gesamten Beispiel unten im Artikel bekommst du das gesamte Beispiel.
TYPES:
td_text TYPE c LENGTH 25,
td_number TYPE i,
td_long TYPE string,
BEGIN OF ts_source,
text1 TYPE td_text,
text3 TYPE td_text,
num1 TYPE td_number,
longtext TYPE td_long,
END OF ts_source,
tt_source TYPE STANDARD TABLE OF ts_source WITH EMPTY KEY,
BEGIN OF ts_target,
ident TYPE td_number,
num TYPE td_number,
longtext TYPE td_long,
text1 TYPE td_text,
text2 TYPE td_text,
END OF ts_target,
tt_target TYPE STANDARD TABLE OF ts_target WITH EMPTY KEY,
tt_target_key TYPE SORTED TABLE OF ts_target WITH UNIQUE KEY ident,
BEGIN OF ts_source_nested,
field1 TYPE td_text,
sub TYPE ts_source,
END OF ts_source_nested,
BEGIN OF ts_target_nested,
field TYPE td_text,
sub TYPE ts_target,
END OF ts_target_nested.
Ein einfacher MOVE der Strukturen sieht daher wie folgt aus, dabei werden nur die gleichnamigen Komponenten verschoben, wodurch die Zielstruktur relativ leer bleibt:
DATA(ls_source) = fill_structure( ).
DATA(ls_target) = CORRESPONDING ts_target( ls_source ).
io_out->write( |Simple move:| ).
io_out->write( ls_target ).
MAPPING
Beim Einsatz eines Mappings können nicht namensgleiche Komponenten mit verschoben werden, dabei wir Quell- und Zielfeld im Mapping angegeben. Zur besseren Hilfe steht dir in Eclipse auch der Content Assist (STRG + LEERTASTE) zur Verfügung:
DATA(ls_source) = fill_structure( ).
DATA(ls_target) = CORRESPONDING ts_target( ls_source MAPPING text2 = text3 num = num1 ).
io_out->write( |Move with mapping:| ).
io_out->write( ls_target ).
Die Ausgabe sieht nun wie folgt aus, die übrigen und vorhandenen Felder wurden nun erfolgreich gemappt und der Zielstruktur zugewiesen:
EXCEPT
Manchmal sollen Felder nach der Zuweisung gelöscht werden, dann wenn es um die Weiterverarbeitung geht oder die Felder leer sein müssen, da sie nicht mehr benötigt werden oder zu Problemen führen können. In der Vergangenheit hat man dazu, nach der Zuweisung, einen Clear der entsprechenden Felder durchgeführt. Dies kann man sich mit dem Zusatz EXCEPT sparen, da dieser dafür sorgt, dass die Felder nicht in die Zielstruktur überführt werden:
DATA(ls_source) = fill_structure( ).
DATA(ls_target) = CORRESPONDING ts_target( ls_source MAPPING text2 = text3 num = num1 EXCEPT longtext ).
io_out->write( |Move without longtext:| ).
io_out->write( ls_target ).
In dem Beispiel oben, haben wir das gleiche Mapping wie vorher durchgeführt, dieses Mal wollen wir allerdings den Langtext nicht mit kopieren. Die Zielstruktur sieht nun wie folgt aus:
DISCARDING DUPLICATES
Es gibt Fälle, bei der eine Standardtabelle in eine sortierte Tabelle mit Schlüssel überführt werden soll. Hier muss man sich aber sicher sein, dass der Schlüssel in der Quelltabelle nur einmal vorhanden ist, da es ansonsten zu einer Ausnahme kommt und die Verarbeitung abbricht. In der Vergangenheit konntest du dies mit mehreren Statements tun (Sortierung der Tabelle und entfernen der Duplikate). In diesem Beispiel befüllen wir die Tabelle unsortiert und mit einem Duplikat und verwenden bei der Zuweisung DISCARDING DUPLICATES, damit werden die Duplikate entfernt und es kommt zu keiner Ausnahme:
DATA(lt_source) = VALUE tt_source(
( num1 = 14 text1 = 'Bread' )
( num1 = 3 text1 = 'Butter' )
( num1 = 7 text1 = 'Jam' )
( num1 = 14 text1 = 'Salt' )
( num1 = 11 text1 = 'Egg' )
( num1 = 19 text1 = 'Coffee' )
).
DATA(lt_target) = CORRESPONDING tt_target_key( lt_source DISCARDING DUPLICATES MAPPING ident = num1 ).
io_out->write( |Move table with key:| ).
io_out->write( lt_target ).
Das Nummernfeld mappen wir entsprechend auf den Schlüssel und entfernen die Duplikate. Der jeweils erste Eintrag wird dabei in die Zieltabelle übernommen und das Ergebnis sieht wie folgt aus:
Nested Mapping
Wie sieht es eigentlich mit verschachtelten Strukturen aus? Hierbei geht es um Strukturen, die auf Feldebene eine weitere Struktur oder Tabelle abbilden und somit mehrere Ebenen haben. Hier kannst du ebenfalls das Mapping anwenden, wie wir es bereits oben getan haben. Wenn du dann die tiefe Struktur ansprechen möchtest, musst du das Element in eine Klammer packen und dort ein eigenes Mapping durchführen:
DATA(ls_source) = VALUE ts_source_nested(
field1 = 'Sub structure'
sub = fill_structure( )
).
DATA(ls_target) = CORRESPONDING ts_target_nested( ls_source
MAPPING field = field1
( sub = sub MAPPING text2 = text3 num = num1 )
).
io_out->write( |Nested move:| ).
io_out->write( ls_target-field ).
io_out->write( ls_target-sub ).
In dem Beispiel haben wir das Hauptfeld gemappt und für die tiefe Struktur ein weiteres Mapping geöffnet. Die Ausgabe muss in zwei Schritten erfolgen, da die Ausgabemethode keine tiefen Strukturen unterstützt:
Gesamtes Beispiel
Wie immer findest du hier das vollständige Beispiel in Form einer ausführbaren ABAP Klasse. Die einzelnen Beispiele sind in einzelne Methoden ausgelagert worden, um für bessere Lesbarkeit zu sorgen:
CLASS zcl_bs_demo_corresponding_deep DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
TYPES:
td_text TYPE c LENGTH 25,
td_number TYPE i,
td_long TYPE string,
BEGIN OF ts_source,
text1 TYPE td_text,
text3 TYPE td_text,
num1 TYPE td_number,
longtext TYPE td_long,
END OF ts_source,
tt_source TYPE STANDARD TABLE OF ts_source WITH EMPTY KEY,
BEGIN OF ts_target,
ident TYPE td_number,
num TYPE td_number,
longtext TYPE td_long,
text1 TYPE td_text,
text2 TYPE td_text,
END OF ts_target,
tt_target TYPE STANDARD TABLE OF ts_target WITH EMPTY KEY,
tt_target_key TYPE SORTED TABLE OF ts_target WITH UNIQUE KEY ident,
BEGIN OF ts_source_nested,
field1 TYPE td_text,
sub TYPE ts_source,
END OF ts_source_nested,
BEGIN OF ts_target_nested,
field TYPE td_text,
sub TYPE ts_target,
END OF ts_target_nested.
PROTECTED SECTION.
PRIVATE SECTION.
METHODS:
fill_structure
RETURNING VALUE(rs_source) TYPE ts_source,
move_simple_structure
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
move_with_mapping
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
move_without_fields
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
move_duplicate_tables
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out,
move_nested_structure
IMPORTING
io_out TYPE REF TO if_oo_adt_classrun_out.
ENDCLASS.
CLASS zcl_bs_demo_corresponding_deep IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
move_simple_structure( out ).
move_with_mapping( out ).
move_without_fields( out ).
move_duplicate_tables( out ).
move_nested_structure( out ).
ENDMETHOD.
METHOD fill_structure.
rs_source = VALUE ts_source(
text1 = 'Some value'
text3 = 'New values'
num1 = 12
longtext = `This is a very long ABAP string in a structure field`
).
ENDMETHOD.
METHOD move_simple_structure.
DATA(ls_source) = fill_structure( ).
DATA(ls_target) = CORRESPONDING ts_target( ls_source ).
io_out->write( |Simple move:| ).
io_out->write( ls_target ).
ENDMETHOD.
METHOD move_with_mapping.
DATA(ls_source) = fill_structure( ).
DATA(ls_target) = CORRESPONDING ts_target( ls_source MAPPING text2 = text3 num = num1 ).
io_out->write( |Move with mapping:| ).
io_out->write( ls_target ).
ENDMETHOD.
METHOD move_without_fields.
DATA(ls_source) = fill_structure( ).
DATA(ls_target) = CORRESPONDING ts_target( ls_source MAPPING text2 = text3 num = num1 EXCEPT longtext ).
io_out->write( |Move without longtext:| ).
io_out->write( ls_target ).
ENDMETHOD.
METHOD move_duplicate_tables.
DATA(lt_source) = VALUE tt_source(
( num1 = 14 text1 = 'Bread' )
( num1 = 3 text1 = 'Butter' )
( num1 = 7 text1 = 'Jam' )
( num1 = 14 text1 = 'Salt' )
( num1 = 11 text1 = 'Egg' )
( num1 = 19 text1 = 'Coffee' )
).
DATA(lt_target) = CORRESPONDING tt_target_key( lt_source DISCARDING DUPLICATES MAPPING ident = num1 ).
io_out->write( |Move table with key:| ).
io_out->write( lt_target ).
ENDMETHOD.
METHOD move_nested_structure.
DATA(ls_source) = VALUE ts_source_nested(
field1 = 'Sub structure'
sub = fill_structure( )
).
DATA(ls_target) = CORRESPONDING ts_target_nested( ls_source
MAPPING field = field1
( sub = sub MAPPING text2 = text3 num = num1 )
).
io_out->write( |Nested move:| ).
io_out->write( ls_target-field ).
io_out->write( ls_target-sub ).
ENDMETHOD.
ENDCLASS.
Fazit
Das neue Statement ist mächtiger als man zu glauben scheint. Zusammen mit dem älteren Artikel zum Thema CORRESPONDING erhältst du den vollen Überblick über das neue Statement und wie du es sinnvoll nutzen kannst.