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
| Methode | Verwendung |
|---|---|
| iXML | DOM-basierte XML-Manipulation |
| Simple Transformation (ST) | ABAP ↔ XML Serialisierung |
| XSLT | Komplexe XML-Transformationen |
| sXML | Stream-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 Factorylo_ixml = cl_ixml=>create( ).
" Neues Dokumentlo_document = lo_ixml->create_document( ).
" Root ElementDATA(lo_root) = lo_document->create_element( name = 'Customers' ).lo_document->append_child( lo_root ).
" Kunden-Element hinzufügenDATA(lo_customer) = lo_document->create_element( name = 'Customer' ).lo_customer->set_attribute( name = 'id' value = '1000' ).lo_root->append_child( lo_customer ).
" Kind-ElementeDATA(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 rendernDATA(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 Inputlv_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 erstellenDATA(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 navigierenDATA(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-CodeTYPES: 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 aufrufenCALL TRANSFORMATION zcustomer_to_xml SOURCE customers = lt_customers RESULT XML lv_xml.
" XML als String anzeigenDATA(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 Inputlv_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 → ABAPCALL TRANSFORMATION zcustomer_to_xml SOURCE XML lv_xml RESULT customers = lt_customers.
" Daten verarbeitenLOOP 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 → ABAPDATA: 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 XMLlv_xml_in = cl_abap_codepage=>convert_to( `<Customers>` && ` <Customer><Id>1000</Id><Name>Mustermann</Name><City>Berlin</City></Customer>` && `</Customers>`).
" XSLT anwendenCALL TRANSFORMATION zformat_customers SOURCE XML lv_xml_in RESULT XML lv_html.
" HTML ausgebenDATA(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 NamespaceDATA(lo_root) = lo_document->create_element_ns( name = 'Order' prefix = 'ord' uri = 'http://example.com/order').lo_document->append_child( lo_root ).
" Namespace-Deklarationlo_root->set_attribute_ns( name = 'xmlns:ord' value = 'http://example.com/order').
" Element im NamespaceDATA(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 ).
" RendernDATA(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>`).
" Parsenlo_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 KundenDATA(lo_xpath) = cl_ixml_xpath=>create( lo_document ).DATA(lo_result) = lo_xpath->evaluate( '//Customer[@country="DE"]' ).
" Ergebnisse durchlaufenDATA(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.
" VerwendungDATA(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-DateienDATA: lv_xml TYPE xstring.
lv_xml = cl_abap_codepage=>convert_to( `<Items><Item>A</Item><Item>B</Item><Item>C</Item></Items>`).
" Reader erstellenDATA(lo_reader) = cl_sxml_string_reader=>create( lv_xml ).
" Stream durchlaufenTRY. 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 → JSONDATA: 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 JSONDATA: 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.
" XMLlv_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>`).
" ValidierenDATA(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 EnvelopeDATA(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 deklarierenlo_envelope->set_attribute_ns( name = 'xmlns:soap' value = 'http://schemas.xmlsoap.org/soap/envelope/').
" BodyDATA(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-ContentDATA(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 ).
" RendernDATA(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 lesenOPEN 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.