This is a test message to test the length of the message box.
Login
ABAP Cloud XML
Erstellt von Software-Heroes

ABAP Cloud - XML erstellen

53

Wie kannst du eigentlich in ABAP Cloud ein XML außerhalb von Transformationen erstellen? In diesem Artikel bauen wir ein XML im Detail nach.

Werbung


In einem älteren Artikel hatten wir uns angeschaut, wie wir einen XML Stream lesen können. In diesem Artikel werden wir auf die Details bei der Erstellung eingehen und uns Schritt für Schritt die Struktur vom letzten Mal zusammenbauen.

 

Einleitung

Ab und zu müssen wir in ABAP auch XML Dateien und Streams verarbeiten, weil wir sie vielleicht aus der Dateiverarbeitung erhalten oder in der ABAP Cloud Welt als Payload an einen Service senden müssen, weil dieser kein JSON versteht. Neben den Transformationen bietet SAP auch verschiedene freigegebene ABAP APIs im System, mit dem wir das umsetzen können. Die Herausforderung bei Transformationen ist, dass sie zusätzlich noch einmal ein eigenes Skillset benötigen, um die Syntax dahinter zu erlernen.

In diesem Artikel wollen wir die Struktur aus dem letzten Artikel nachbauen. Die Struktur enthält verschiedene Bestandteile, wie einen eigenen Namespace, Attribute und Werte auf verschiedenen Ebenen.

 

Hinweis: Erklärung zu den einzelnen Bestandteilen des XML findest du in dem oben verlinkten Artikel, um den Aufbau des XML erst einmal besser zu verstehen.

 

Einstieg

Im ersten Schritt benötigen wir eine Konfiguration und einige Daten, um mit der Umsetzung beginnen zu können. Dabei verwenden wir den gleichen Typen aus dem letzten Artikel, den wir uns lokal in der Klasse anlegen.

TYPES: BEGIN OF people,
         height TYPE c LENGTH 9,
         name   TYPE string,
       END OF people.
TYPES peoples TYPE STANDARD TABLE OF people WITH EMPTY KEY.

TYPES: BEGIN OF file,
         head_key         TYPE c LENGTH 10,
         head_description TYPE string,
         title            TYPE string,
         description      TYPE string,
         desc_length      TYPE i,
         desc_space       TYPE c LENGTH 20,
         tags             TYPE string,
         peoples          TYPE peoples,
       END OF file.

 

Danach befüllen wir die Konfiguration und übergeben sie an die Generierungsfunktion, die uns dann das XML erstellt. Die Daten in unserem Beispiel würden damit wie folgt aussehen.

DATA(setting) = VALUE file( head_key         = 'H1'
                            head_description = `My custom XML`
                            title            = `First try`
                            description      = `A description text`
                            desc_length      = 25
                            desc_space       = 'minimum'
                            tags             = `Name, Data, Others`
                            peoples          = VALUE #( ( height = '150cm' name = `Jason` )
                                                        ( height = '155cm' name = `Pamela` )
                                                        ( height = '190cm' name = `Ryan` ) ) ).

 

Umsetzung

In diesem Kapitel gehen wir auf das Mapping und die Umsetzung ein. Dabei verwenden wir die Klasse CL_SXML_STRING_WRITER um unser XML zu erzeugen. In den weiteren Abschnitten gehen wir dann auf die verschiedenen Besonderheiten ein.

 

Writer

Damit wir mit der Erstellung beginnen können, legen wir einen String Writer Objekt an, dieses benötigen wir nur lokal und legen es als lokale Variable an.

DATA(string_writer) = cl_sxml_string_writer=>create( ).
writer = CAST if_sxml_writer( string_writer ).

 

Der String Writer hat eine recht kleine Anzahl an Methoden und dient vor allem zur Erzeugung der Datei. Deshalb müssen wir einen CAST auf IF_SXML_WRITER durchführen. Die Instanz legen wir uns als Attribut in der Klasse ab, da wir aus den verschiedenen Methoden darauf zugreifen wollen. Grundsätzlich kannst du die Instanz auch von Methode zu Methode weitergeben.

 

Das Interface bietet den größten Teil der Methoden die wir für die Arbeit mit dem XML Objekt benötigen.

 

Struktur

Damit wir nun unsere Struktur abarbeiten können, bilden wir die verschiedenen Schritte als Methoden ab und rufen diese nacheinander auf. Damit zerlegen wir die große Aufgabe in kleine Bestandteile und können Knoten für Knoten das Mapping abarbeiten.

open_root( ).
add_title( ).
add_description( ).
add_people( ).
add_tags( ).
close_node( ).

 

Die Methode CLOSE_NODE legen wir zentral an, da wir diese zum Schließen eines Knotens mehrfach benötigen und so einfacher als Funktion wiederverwenden können. Die Methode erzeugt ein schließendes Element und schreibt es über den WRITER in das Objekt.

writer->write_node( writer->new_close_element( ) ).

 

Knoten mit Attributen

Unser Wurzelknoten besteht aus einem Namespace und zwei Attributen. Entsprechend lassen wir uns vom WRITER ein neues öffnendes Element anlegen und übergeben den Namen, den Namespace und das Präfix des Namespaces. Über das erzeugte Objekt können wir die Attribute mit ihren Werten setzen. Damit wir den Knoten übernehmen, rufen wir am Ende die Methode WRITE_NODE auf und übergeben unser Objekt.

DATA(open_node) = writer->new_open_element( name   = `myroot`
                                            nsuri  = namespace_swh
                                            prefix = prefix_swh ).
open_node->set_attribute( name  = `key`
                          value = run_setting-title ).
open_node->set_attribute( name  = `description`
                          value = run_setting-description ).
writer->write_node( open_node ).

 

Führen wir die bisherige Logik (Root Knoten und Schließen) aus, dann erhalten wir aktuell das folgende Ergebnis in Form unseres ersten Knotens.

<swh:myroot key="First try" description="A description text" xmlns:swh="http://software-heroes/swh"/>

 

Der Namespace wurde in den Knoten übernommen und auch alle Attribute finden wir wieder. Damit können wir beginnen die weiteren Knoten in der gleichen Form zu erzeugen.

 

Knoten mit Wert

Wie sieht es nun aus, wenn wir einen Wert schreiben wollen? Der Wert befindet sich zwischen den beiden Tags. In diesem Beispiel erzeugen wir das TITLE Tag und übernehmen den Knoten ins Modell. Im nächsten Schritt erzeugen wir über NEW_VALUE einen Knoten für den Wert, übernehmen den Wert mit SET_VALUE und fügen diesen ebenfalls hinzu. Zum Abschluss schließen wir den aktuellen Knoten.

DATA(open_node) = writer->new_open_element( name   = `title`
                                            nsuri  = namespace_swh
                                            prefix = prefix_swh ).
writer->write_node( open_node ).

DATA(value_node) = writer->new_value( ).
value_node->set_value( run_setting-title ).
writer->write_node( value_node ).

close_node( ).

 

Abschluss

Wenn wir alle Informationen und Knoten erzeugt haben, dann können wir über das Original-Objekt die Methode GET_OUTPUT aufrufen, die uns das XML als Binary zurückgibt. Über die XCO Klasse wandeln wir den Inhalt nun in einen lesbaren String um. Bevor wir das Ergebnis zurückgeben, um es zu prüfen, fügen wir noch das XML Tag am Anfang hinzu, dieses wird leider nicht mit erzeugt.

DATA(binary) = string_writer->get_output( ).
DATA(content) = xco_cp=>xstring( binary )->as_string( xco_cp_character=>code_page->utf_8 )->value.

RETURN |<?xml version="1.0" encoding="UTF-8"?>{ content }|.

 

Schauen wir uns das Ergebnis an, dann solltest du nun die folgende Ausgabe haben. In diesem Fall haben wir uns das Ergebnis im Browser rendern lassen, da die Klasse den String unformatiert zurückgibt.

Optionen

Im letzten Schritt hatten wir den Header manuell an den Stream hinzugefügt, davon sind vor allem ältere Releases betroffen. Ab dem Release 2502 ist es nun möglich, den Header zu erzeugen, ob mit oder ohne Encoding. Dazu stehen die beiden Konstanten zur Verfügung:

  • IF_SXML_WRITER=>CO_OPT_VAL_FULL generiert den Kompletten Header (<?xml version="1.0" encoding="utf-8"?>)
  • IF_SXML_WRITER=>CO_OPT_VAL_WITHOUT_ENCODING generiert die Ausgabe ohne das entsprechende Encoding (IF_SXML_WRITER=>CO_OPT_VAL_FULL)

 

In unserem Beispiel können wir neben dem Header auch weitere Optionen für die Ausgabe definieren. Setzen wir zum Beispiel Zeilenumbrüche und eine Einrückung, dann können wir das Ergebnis auch in die Konsole ausgeben.

writer->set_option( option = if_sxml_writer=>co_opt_xml_header
                    value  = if_sxml_writer=>co_opt_val_full ).

writer->set_option( option = if_sxml_writer=>co_opt_linebreaks
                    value  = abap_true ).

writer->set_option( option = if_sxml_writer=>co_opt_indent
                    value  = abap_true ).

 

In der ABAP Konsole würde dann das finale Ergebnis wie folgt aussehen.

 

Komplettes Beispiel

Wir sind in diesem Artikel nicht auf alle Einzelheiten der Konvertierung eingegangen. Die vollständige Klasse findest du allerdings hier, wenn du es bei dir im System testen willst oder noch weitere Informationen zur Klasse suchst.

CLASS zcl_bs_demo_xml_create DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PRIVATE SECTION.
    TYPES: BEGIN OF people,
             height TYPE c LENGTH 9,
             name   TYPE string,
           END OF people.
    TYPES peoples TYPE STANDARD TABLE OF people WITH EMPTY KEY.

    TYPES: BEGIN OF file,
             head_key         TYPE c LENGTH 10,
             head_description TYPE string,
             title            TYPE string,
             description      TYPE string,
             desc_length      TYPE i,
             desc_space       TYPE c LENGTH 20,
             tags             TYPE string,
             peoples          TYPE peoples,
           END OF file.

    CONSTANTS namespace_swh TYPE string VALUE `http://software-heroes/swh`.
    CONSTANTS prefix_swh    TYPE string VALUE `swh`.

    DATA run_setting TYPE file.
    DATA writer      TYPE REF TO if_sxml_writer.

    METHODS create_xml_content
      IMPORTING run_setting   TYPE file
      RETURNING VALUE(result) TYPE string.

    METHODS add_person
      IMPORTING !person TYPE zcl_bs_demo_xml_create=>people.

    METHODS close_node.
    METHODS open_root.
    METHODS add_title.
    METHODS add_description.
    METHODS add_people.
    METHODS add_tags.
    METHODS set_writer_options.

ENDCLASS.


CLASS zcl_bs_demo_xml_create IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    DATA(setting) = VALUE file( head_key         = 'H1'
                                head_description = `My custom XML`
                                title            = `First try`
                                description      = `A description text`
                                desc_length      = 25
                                desc_space       = 'minimum'
                                tags             = `Name, Data, Others`
                                peoples          = VALUE #( ( height = '150cm' name = `Jason` )
                                                            ( height = '155cm' name = `Pamela` )
                                                            ( height = '190cm' name = `Ryan` ) ) ).

    out->write( create_xml_content( setting ) ).
  ENDMETHOD.


  METHOD create_xml_content.
    me->run_setting = run_setting.

    DATA(string_writer) = cl_sxml_string_writer=>create( ).
    writer = CAST if_sxml_writer( string_writer ).

    set_writer_options( ).

    open_root( ).
    add_title( ).
    add_description( ).
    add_people( ).
    add_tags( ).
    close_node( ).

    DATA(binary) = string_writer->get_output( ).
    DATA(content) = xco_cp=>xstring( binary )->as_string( xco_cp_character=>code_page->utf_8 )->value.

    " RETURN |<?xml version="1.0" encoding="UTF-8"?>{ content }|.
    RETURN content.
  ENDMETHOD.


  METHOD set_writer_options.
    writer->set_option( option = if_sxml_writer=>co_opt_xml_header
                        value  = if_sxml_writer=>co_opt_val_full ).

    writer->set_option( option = if_sxml_writer=>co_opt_linebreaks
                        value  = abap_true ).

    writer->set_option( option = if_sxml_writer=>co_opt_indent
                        value  = abap_true ).
  ENDMETHOD.


  METHOD close_node.
    writer->write_node( writer->new_close_element( ) ).
  ENDMETHOD.


  METHOD open_root.
    DATA(open_node) = writer->new_open_element( name   = `myroot`
                                                nsuri  = namespace_swh
                                                prefix = prefix_swh ).
    open_node->set_attribute( name  = `key`
                              value = run_setting-title ).
    open_node->set_attribute( name  = `description`
                              value = run_setting-description ).
    writer->write_node( open_node ).
  ENDMETHOD.


  METHOD add_title.
    DATA(open_node) = writer->new_open_element( name   = `title`
                                                nsuri  = namespace_swh
                                                prefix = prefix_swh ).
    writer->write_node( open_node ).

    DATA(value_node) = writer->new_value( ).
    value_node->set_value( run_setting-title ).
    writer->write_node( value_node ).

    close_node( ).
  ENDMETHOD.


  METHOD add_description.
    DATA(open_node) = writer->new_open_element( name   = `description`
                                                nsuri  = namespace_swh
                                                prefix = prefix_swh ).
    open_node->set_attribute( name  = `length`
                              value = CONV #( run_setting-desc_length ) ).
    open_node->set_attribute( name  = `space`
                              value = CONV #( run_setting-desc_space ) ).
    writer->write_node( open_node ).

    DATA(value_node) = writer->new_value( ).
    value_node->set_value( run_setting-description ).
    writer->write_node( value_node ).

    close_node( ).
  ENDMETHOD.


  METHOD add_people.
    DATA(open_node) = writer->new_open_element( `table` ).
    writer->write_node( open_node ).

    LOOP AT run_setting-peoples INTO DATA(person).
      add_person( person ).
    ENDLOOP.

    close_node( ).
  ENDMETHOD.


  METHOD add_tags.
    DATA(open_node) = writer->new_open_element( name   = `tags`
                                                nsuri  = namespace_swh
                                                prefix = prefix_swh ).
    writer->write_node( open_node ).

    DATA(value_node) = writer->new_value( ).
    value_node->set_value( run_setting-tags ).
    writer->write_node( value_node ).

    close_node( ).
  ENDMETHOD.


  METHOD add_person.
    DATA(open_node) = writer->new_open_element( `item` ).
    open_node->set_attribute( name  = `height`
                              value = CONV #( person-height ) ).
    writer->write_node( open_node ).

    DATA(value_node) = writer->new_value( ).
    value_node->set_value( person-name ).
    writer->write_node( value_node ).

    close_node( ).
  ENDMETHOD.
ENDCLASS.

 

Fazit

Aktuell gibt es viele Dinge in der ABAP Entwicklung zu lernen, bist du neu, wird es dir vielleicht schwer fallen alle Dinge auf einmal zu lernen. Du kannst dich deshalb entscheiden, dir die Klasse anzuschauen oder mit dem Thema Transformation vertraut zu machen.

 

Weitere Informationen:
SAP Help - sXML Rendering


Enthaltene Themen:
ABAP CloudABAPXMLXML erstellen
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


ABAP Cloud - Transport der Software-Komponente

Kategorie - ABAP

Wie sieht es eigentlich mit dem Transport von Software Komponenten im ABAP Cloud aus? Benötigst du die Komponente On-Premise auch im Test- und Produktivsystem?

11.03.2025

ABAP Cloud - CRV Update & TIER-3

Kategorie - ABAP

Wie kannst du in ABAP Cloud die richtige API für dein Szenario finden und was machst du eigentlich mit TIER-3 in deiner Entwicklung? Mehr Informationen hier.

25.02.2025

ABAP Cloud - XML lesen

Kategorie - ABAP

Wie kannst du in ABAP Cloud relativ einfach XML Daten lesen und verarbeiten? Dazu schauen wir uns ein Beispiel an und gehen das Schritt für Schritt durch.

21.02.2025

ABAP Cloud - Clean Core (Szenarien)

Kategorie - ABAP

Lass uns in diesem Artikel noch einmal die Clean Core Architektur mit ABAP Cloud anschauen, wo diese eingesetzt wird und wo du deine Anwendungen bauen kannst.

10.01.2025

ABAP Cloud - Programmiermodell

Kategorie - ABAP

Welches Programmiermodell kommt mit ABAP Cloud zum Einsatz und was können wir aus dem Vorgänger lernen? Mehr Details im Artikel.

03.01.2025