ABAP XML Processing: iXML, CALL TRANSFORMATION, XSLT

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

XML Processing in ABAP bietet mehrere Ansätze: Die iXML-Bibliothek für DOM-basierte Verarbeitung, Simple Transformations für einfache Serialisierung und XSLT für komplexe Transformationen.

Methoden im Überblick

MethodeVerwendung
iXMLDOM-basierte XML-Manipulation
Simple Transformation (ST)ABAP ↔ XML Serialisierung
XSLTKomplexe XML-Transformationen
sXMLStream-basiertes Parsen

Beispiele

1. XML mit iXML erstellen

DATA: lo_ixml TYPE REF TO if_ixml,
lo_document TYPE REF TO if_ixml_document,
lo_element TYPE REF TO if_ixml_element,
lv_xml TYPE xstring.
" iXML Factory
lo_ixml = cl_ixml=>create( ).
" Neues Dokument
lo_document = lo_ixml->create_document( ).
" Root Element
DATA(lo_root) = lo_document->create_element( name = 'Customers' ).
lo_document->append_child( lo_root ).
" Kunden-Element hinzufügen
DATA(lo_customer) = lo_document->create_element( name = 'Customer' ).
lo_customer->set_attribute( name = 'id' value = '1000' ).
lo_root->append_child( lo_customer ).
" Kind-Elemente
DATA(lo_name) = lo_document->create_element( name = 'Name' ).
lo_name->set_value( 'Mustermann GmbH' ).
lo_customer->append_child( lo_name ).
DATA(lo_city) = lo_document->create_element( name = 'City' ).
lo_city->set_value( 'Berlin' ).
lo_customer->append_child( lo_city ).
" Als String rendern
DATA(lo_stream) = lo_ixml->create_stream_factory( )->create_ostream_xstring( lv_xml ).
DATA(lo_renderer) = lo_ixml->create_renderer(
document = lo_document
ostream = lo_stream
).
lo_renderer->render( ).
" Ergebnis:
" <Customers>
" <Customer id="1000">
" <Name>Mustermann GmbH</Name>
" <City>Berlin</City>
" </Customer>
" </Customers>
DATA(lv_xml_string) = cl_abap_codepage=>convert_from( lv_xml ).
WRITE: / lv_xml_string.

2. XML mit iXML parsen

DATA: lv_xml TYPE xstring.
" XML als Input
lv_xml = cl_abap_codepage=>convert_to(
`<Customers>` &&
` <Customer id="1000">` &&
` <Name>Mustermann GmbH</Name>` &&
` <City>Berlin</City>` &&
` </Customer>` &&
` <Customer id="1001">` &&
` <Name>Schmidt AG</Name>` &&
` <City>Hamburg</City>` &&
` </Customer>` &&
`</Customers>`
).
" Parser erstellen
DATA(lo_ixml) = cl_ixml=>create( ).
DATA(lo_stream) = lo_ixml->create_stream_factory( )->create_istream_xstring( lv_xml ).
DATA(lo_document) = lo_ixml->create_document( ).
DATA(lo_parser) = lo_ixml->create_parser(
stream_factory = lo_ixml->create_stream_factory( )
istream = lo_stream
document = lo_document
).
IF lo_parser->parse( ) <> 0.
" Fehler beim Parsen
DATA(lv_error) = lo_parser->get_error( )->get_reason( ).
WRITE: / 'Parse Error:', lv_error.
RETURN.
ENDIF.
" Durch Elemente navigieren
DATA(lo_root) = lo_document->get_root_element( ).
DATA(lo_customers) = lo_root->get_children( ).
DATA(lo_iterator) = lo_customers->create_iterator( ).
DATA(lo_node) = lo_iterator->get_next( ).
WHILE lo_node IS BOUND.
IF lo_node->get_type( ) = if_ixml_node=>co_node_element.
DATA(lo_customer) = CAST if_ixml_element( lo_node ).
DATA(lv_id) = lo_customer->get_attribute( 'id' ).
" Kind-Elemente lesen
DATA(lo_name_node) = lo_customer->find_from_name( 'Name' ).
DATA(lo_city_node) = lo_customer->find_from_name( 'City' ).
WRITE: / 'ID:', lv_id,
/ 'Name:', lo_name_node->get_value( ),
/ 'City:', lo_city_node->get_value( ).
SKIP.
ENDIF.
lo_node = lo_iterator->get_next( ).
ENDWHILE.

3. Simple Transformation (ABAP → XML)

" Simple Transformation in SE80 erstellen:
" Name: ZCUSTOMER_TO_XML
" <?sap.transform simple?>
" <tt:transform xmlns:tt="http://www.sap.com/transformation-templates">
" <tt:root name="CUSTOMERS"/>
" <tt:template>
" <Customers>
" <tt:loop ref=".CUSTOMERS">
" <Customer>
" <Id><tt:value ref="KUNNR"/></Id>
" <Name><tt:value ref="NAME1"/></Name>
" <City><tt:value ref="ORT01"/></City>
" </Customer>
" </tt:loop>
" </Customers>
" </tt:template>
" </tt:transform>
" ABAP-Code
TYPES: BEGIN OF ty_customer,
kunnr TYPE kunnr,
name1 TYPE name1_gp,
ort01 TYPE ort01_gp,
END OF ty_customer.
DATA: lt_customers TYPE STANDARD TABLE OF ty_customer,
lv_xml TYPE xstring.
lt_customers = VALUE #(
( kunnr = '1000' name1 = 'Mustermann GmbH' ort01 = 'Berlin' )
( kunnr = '1001' name1 = 'Schmidt AG' ort01 = 'Hamburg' )
).
" Transformation aufrufen
CALL TRANSFORMATION zcustomer_to_xml
SOURCE customers = lt_customers
RESULT XML lv_xml.
" XML als String anzeigen
DATA(lv_xml_string) = cl_abap_codepage=>convert_from( lv_xml ).
WRITE: / lv_xml_string.

4. Simple Transformation (XML → ABAP)

" Umgekehrte Simple Transformation (oder gleiche verwenden)
DATA: lt_customers TYPE STANDARD TABLE OF ty_customer,
lv_xml TYPE xstring.
" XML Input
lv_xml = cl_abap_codepage=>convert_to(
`<Customers>` &&
` <Customer><Id>1000</Id><Name>Mustermann GmbH</Name><City>Berlin</City></Customer>` &&
` <Customer><Id>1001</Id><Name>Schmidt AG</Name><City>Hamburg</City></Customer>` &&
`</Customers>`
).
" XML → ABAP
CALL TRANSFORMATION zcustomer_to_xml
SOURCE XML lv_xml
RESULT customers = lt_customers.
" Daten verarbeiten
LOOP AT lt_customers INTO DATA(ls_customer).
WRITE: / ls_customer-kunnr, ls_customer-name1, ls_customer-ort01.
ENDLOOP.

5. ID-Transformation (automatische Serialisierung)

" ID = Identity Transformation (Standard)
" Serialisiert automatisch ABAP-Daten zu asXML
DATA: ls_customer TYPE kna1,
lv_xml TYPE xstring.
ls_customer = VALUE #(
kunnr = '0000001000'
name1 = 'Test Customer'
ort01 = 'Berlin'
land1 = 'DE'
).
" ABAP → XML (asXML Format)
CALL TRANSFORMATION id
SOURCE customer = ls_customer
RESULT XML lv_xml.
" XML → ABAP
DATA: ls_customer_back TYPE kna1.
CALL TRANSFORMATION id
SOURCE XML lv_xml
RESULT customer = ls_customer_back.

6. XSLT Transformation

" XSLT in SE80 erstellen:
" Name: ZFORMAT_CUSTOMERS
" <xsl:transform version="1.0"
" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
" <xsl:template match="/">
" <html>
" <body>
" <h1>Kundenliste</h1>
" <table border="1">
" <tr><th>ID</th><th>Name</th><th>Stadt</th></tr>
" <xsl:for-each select="//Customer">
" <tr>
" <td><xsl:value-of select="Id"/></td>
" <td><xsl:value-of select="Name"/></td>
" <td><xsl:value-of select="City"/></td>
" </tr>
" </xsl:for-each>
" </table>
" </body>
" </html>
" </xsl:template>
" </xsl:transform>
DATA: lv_xml_in TYPE xstring,
lv_html TYPE xstring.
" Input XML
lv_xml_in = cl_abap_codepage=>convert_to(
`<Customers>` &&
` <Customer><Id>1000</Id><Name>Mustermann</Name><City>Berlin</City></Customer>` &&
`</Customers>`
).
" XSLT anwenden
CALL TRANSFORMATION zformat_customers
SOURCE XML lv_xml_in
RESULT XML lv_html.
" HTML ausgeben
DATA(lv_html_string) = cl_abap_codepage=>convert_from( lv_html ).

7. XML mit Namespaces

DATA: lo_ixml TYPE REF TO if_ixml,
lo_document TYPE REF TO if_ixml_document,
lv_xml TYPE xstring.
lo_ixml = cl_ixml=>create( ).
lo_document = lo_ixml->create_document( ).
" Root mit Namespace
DATA(lo_root) = lo_document->create_element_ns(
name = 'Order'
prefix = 'ord'
uri = 'http://example.com/order'
).
lo_document->append_child( lo_root ).
" Namespace-Deklaration
lo_root->set_attribute_ns(
name = 'xmlns:ord'
value = 'http://example.com/order'
).
" Element im Namespace
DATA(lo_item) = lo_document->create_element_ns(
name = 'Item'
prefix = 'ord'
uri = 'http://example.com/order'
).
lo_item->set_value( 'Product A' ).
lo_root->append_child( lo_item ).
" Rendern
DATA(lo_stream) = lo_ixml->create_stream_factory( )->create_ostream_xstring( lv_xml ).
lo_ixml->create_renderer( document = lo_document ostream = lo_stream )->render( ).
" Ergebnis:
" <ord:Order xmlns:ord="http://example.com/order">
" <ord:Item>Product A</ord:Item>
" </ord:Order>

8. XPath-Abfragen

DATA: lv_xml TYPE xstring,
lo_ixml TYPE REF TO if_ixml,
lo_document TYPE REF TO if_ixml_document.
lv_xml = cl_abap_codepage=>convert_to(
`<Customers>` &&
` <Customer id="1000" country="DE"><Name>Müller</Name></Customer>` &&
` <Customer id="1001" country="AT"><Name>Schmidt</Name></Customer>` &&
` <Customer id="1002" country="DE"><Name>Weber</Name></Customer>` &&
`</Customers>`
).
" Parsen
lo_ixml = cl_ixml=>create( ).
lo_document = lo_ixml->create_document( ).
DATA(lo_parser) = lo_ixml->create_parser(
stream_factory = lo_ixml->create_stream_factory( )
istream = lo_ixml->create_stream_factory( )->create_istream_xstring( lv_xml )
document = lo_document
).
lo_parser->parse( ).
" XPath-Abfrage: Alle deutschen Kunden
DATA(lo_xpath) = cl_ixml_xpath=>create( lo_document ).
DATA(lo_result) = lo_xpath->evaluate( '//Customer[@country="DE"]' ).
" Ergebnisse durchlaufen
DATA(lo_iter) = lo_result->create_iterator( ).
DATA(lo_node) = lo_iter->get_next( ).
WHILE lo_node IS BOUND.
DATA(lo_element) = CAST if_ixml_element( lo_node ).
DATA(lo_name) = lo_element->find_from_name( 'Name' ).
WRITE: / 'DE Kunde:', lo_name->get_value( ).
lo_node = lo_iter->get_next( ).
ENDWHILE.

9. XML-Klasse für einfache Verarbeitung

CLASS zcl_xml_helper DEFINITION.
PUBLIC SECTION.
METHODS: constructor
IMPORTING iv_xml TYPE xstring OPTIONAL.
METHODS: create_document.
METHODS: add_element
IMPORTING iv_name TYPE string
iv_value TYPE string OPTIONAL
iv_parent TYPE string OPTIONAL
RETURNING VALUE(ro_element) TYPE REF TO if_ixml_element.
METHODS: get_value
IMPORTING iv_xpath TYPE string
RETURNING VALUE(rv_value) TYPE string.
METHODS: get_xml
RETURNING VALUE(rv_xml) TYPE xstring.
METHODS: get_xml_string
RETURNING VALUE(rv_xml) TYPE string.
PRIVATE SECTION.
DATA: mo_ixml TYPE REF TO if_ixml,
mo_document TYPE REF TO if_ixml_document.
ENDCLASS.
CLASS zcl_xml_helper IMPLEMENTATION.
METHOD constructor.
mo_ixml = cl_ixml=>create( ).
IF iv_xml IS NOT INITIAL.
" XML parsen
mo_document = mo_ixml->create_document( ).
DATA(lo_parser) = mo_ixml->create_parser(
stream_factory = mo_ixml->create_stream_factory( )
istream = mo_ixml->create_stream_factory( )->create_istream_xstring( iv_xml )
document = mo_document
).
lo_parser->parse( ).
ENDIF.
ENDMETHOD.
METHOD create_document.
mo_document = mo_ixml->create_document( ).
ENDMETHOD.
METHOD add_element.
ro_element = mo_document->create_element( CONV #( iv_name ) ).
IF iv_value IS NOT INITIAL.
ro_element->set_value( CONV #( iv_value ) ).
ENDIF.
IF iv_parent IS INITIAL.
mo_document->append_child( ro_element ).
ELSE.
DATA(lo_parent) = mo_document->find_from_name( CONV #( iv_parent ) ).
IF lo_parent IS BOUND.
lo_parent->append_child( ro_element ).
ENDIF.
ENDIF.
ENDMETHOD.
METHOD get_value.
DATA(lo_xpath) = cl_ixml_xpath=>create( mo_document ).
DATA(lo_result) = lo_xpath->evaluate( CONV #( iv_xpath ) ).
DATA(lo_node) = lo_result->get_item( 0 ).
IF lo_node IS BOUND.
rv_value = lo_node->get_value( ).
ENDIF.
ENDMETHOD.
METHOD get_xml.
DATA(lo_stream) = mo_ixml->create_stream_factory( )->create_ostream_xstring( rv_xml ).
mo_ixml->create_renderer( document = mo_document ostream = lo_stream )->render( ).
ENDMETHOD.
METHOD get_xml_string.
rv_xml = cl_abap_codepage=>convert_from( get_xml( ) ).
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA(lo_xml) = NEW zcl_xml_helper( ).
lo_xml->create_document( ).
lo_xml->add_element( iv_name = 'Root' ).
lo_xml->add_element( iv_name = 'Item' iv_value = 'Test' iv_parent = 'Root' ).
DATA(lv_result) = lo_xml->get_xml_string( ).

10. sXML - Stream-basiertes Parsen

" Effizient für große XML-Dateien
DATA: lv_xml TYPE xstring.
lv_xml = cl_abap_codepage=>convert_to(
`<Items><Item>A</Item><Item>B</Item><Item>C</Item></Items>`
).
" Reader erstellen
DATA(lo_reader) = cl_sxml_string_reader=>create( lv_xml ).
" Stream durchlaufen
TRY.
DO.
DATA(lo_node) = lo_reader->read_next_node( ).
IF lo_node IS NOT BOUND.
EXIT.
ENDIF.
CASE lo_node->type.
WHEN if_sxml_node=>co_nt_element_open.
DATA(lo_open) = CAST if_sxml_open_element( lo_node ).
WRITE: / 'Element Start:', lo_open->qname-name.
WHEN if_sxml_node=>co_nt_element_close.
DATA(lo_close) = CAST if_sxml_close_element( lo_node ).
WRITE: / 'Element Ende:', lo_close->qname-name.
WHEN if_sxml_node=>co_nt_value.
DATA(lo_value) = CAST if_sxml_value_node( lo_node ).
WRITE: / 'Wert:', lo_value->get_value( ).
ENDCASE.
ENDDO.
CATCH cx_sxml_parse_error INTO DATA(lx_error).
WRITE: / 'Parse Error:', lx_error->get_text( ).
ENDTRY.

11. XML mit JSON konvertieren

" XML → JSON
DATA: lv_xml TYPE xstring,
lv_json TYPE string.
lv_xml = cl_abap_codepage=>convert_to(
`<Customer><Id>1000</Id><Name>Test</Name></Customer>`
).
" Erst in ABAP, dann nach JSON
DATA: BEGIN OF ls_customer,
id TYPE string,
name TYPE string,
END OF ls_customer.
CALL TRANSFORMATION id
SOURCE XML lv_xml
RESULT customer = ls_customer.
lv_json = /ui2/cl_json=>serialize( data = ls_customer ).
" {"ID":"1000","NAME":"Test"}

12. XML-Schema-Validierung

DATA: lv_xml TYPE xstring,
lv_schema TYPE xstring.
" XML
lv_xml = cl_abap_codepage=>convert_to(
`<Customer><Name>Test</Name></Customer>`
).
" Schema (XSD)
lv_schema = cl_abap_codepage=>convert_to(
`<?xml version="1.0"?>` &&
`<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">` &&
` <xs:element name="Customer">` &&
` <xs:complexType>` &&
` <xs:sequence>` &&
` <xs:element name="Name" type="xs:string"/>` &&
` </xs:sequence>` &&
` </xs:complexType>` &&
` </xs:element>` &&
`</xs:schema>`
).
" Validieren
DATA(lo_ixml) = cl_ixml=>create( ).
DATA(lo_schema_factory) = lo_ixml->create_schema_factory( ).
TRY.
DATA(lo_schema) = lo_schema_factory->create_schema_from_stream(
lo_ixml->create_stream_factory( )->create_istream_xstring( lv_schema )
).
DATA(lo_document) = lo_ixml->create_document( ).
DATA(lo_parser) = lo_ixml->create_parser(
stream_factory = lo_ixml->create_stream_factory( )
istream = lo_ixml->create_stream_factory( )->create_istream_xstring( lv_xml )
document = lo_document
).
lo_parser->set_schema( lo_schema ).
IF lo_parser->parse( ) = 0.
WRITE: / 'XML ist valide'.
ELSE.
WRITE: / 'XML ist nicht valide'.
ENDIF.
CATCH cx_root INTO DATA(lx_error).
WRITE: / 'Fehler:', lx_error->get_text( ).
ENDTRY.

13. Simple Transformation mit Conditions

" Simple Transformation mit Bedingungen:
" <?sap.transform simple?>
" <tt:transform xmlns:tt="http://www.sap.com/transformation-templates">
" <tt:root name="ORDERS"/>
" <tt:template>
" <Orders>
" <tt:loop ref=".ORDERS">
" <tt:cond check="not-initial(STATUS)">
" <Order status="active">
" <Id><tt:value ref="VBELN"/></Id>
" </Order>
" </tt:cond>
" <tt:cond check="initial(STATUS)">
" <Order status="inactive">
" <Id><tt:value ref="VBELN"/></Id>
" </Order>
" </tt:cond>
" </tt:loop>
" </Orders>
" </tt:template>
" </tt:transform>

14. SOAP-Envelope erstellen

DATA: lo_ixml TYPE REF TO if_ixml,
lo_document TYPE REF TO if_ixml_document,
lv_xml TYPE xstring.
lo_ixml = cl_ixml=>create( ).
lo_document = lo_ixml->create_document( ).
" SOAP Envelope
DATA(lo_envelope) = lo_document->create_element_ns(
name = 'Envelope'
prefix = 'soap'
uri = 'http://schemas.xmlsoap.org/soap/envelope/'
).
lo_document->append_child( lo_envelope ).
" Namespace deklarieren
lo_envelope->set_attribute_ns(
name = 'xmlns:soap'
value = 'http://schemas.xmlsoap.org/soap/envelope/'
).
" Body
DATA(lo_body) = lo_document->create_element_ns(
name = 'Body'
prefix = 'soap'
uri = 'http://schemas.xmlsoap.org/soap/envelope/'
).
lo_envelope->append_child( lo_body ).
" Request-Content
DATA(lo_request) = lo_document->create_element( 'GetCustomerRequest' ).
lo_body->append_child( lo_request ).
DATA(lo_id) = lo_document->create_element( 'CustomerId' ).
lo_id->set_value( '1000' ).
lo_request->append_child( lo_id ).
" Rendern
DATA(lo_stream) = lo_ixml->create_stream_factory( )->create_ostream_xstring( lv_xml ).
lo_ixml->create_renderer( document = lo_document ostream = lo_stream )->render( ).

15. XML-Datei lesen und verarbeiten

DATA: lv_file TYPE string VALUE '/tmp/customers.xml',
lv_xml TYPE xstring,
lv_content TYPE string.
" Datei lesen
OPEN DATASET lv_file FOR INPUT IN BINARY MODE.
IF sy-subrc = 0.
READ DATASET lv_file INTO lv_xml.
CLOSE DATASET lv_file.
" XML verarbeiten
DATA(lo_xml) = NEW zcl_xml_helper( iv_xml = lv_xml ).
" Werte extrahieren
DATA(lv_name) = lo_xml->get_value( '//Customer/Name' ).
DATA(lv_city) = lo_xml->get_value( '//Customer/City' ).
WRITE: / 'Name:', lv_name,
/ 'Stadt:', lv_city.
ENDIF.

Wichtige Hinweise / Best Practice

  • Simple Transformations für einfache ABAP ↔ XML Konvertierung.
  • iXML für komplexe DOM-Manipulation.
  • sXML für große Dateien (Stream-basiert, weniger Speicher).
  • XSLT für komplexe Transformationen und HTML-Generierung.
  • ID-Transformation für schnelle Serialisierung (asXML Format).
  • Namespaces korrekt deklarieren und verwenden.
  • Encoding beachten (UTF-8 empfohlen).
  • XPath für effiziente Abfragen nutzen.
  • Fehlerbehandlung bei Parse-Operationen implementieren.
  • Kombinieren Sie mit JSON Processing für REST-APIs.