Traitement XML en ABAP : iXML, CALL TRANSFORMATION, XSLT

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

Le traitement XML en ABAP offre plusieurs approches : la bibliotheque iXML pour le traitement base sur DOM, les Simple Transformations pour une serialisation simple et XSLT pour des transformations complexes.

Apercu des methodes

MethodeUtilisation
iXMLManipulation XML basee sur DOM
Simple Transformation (ST)Serialisation ABAP <-> XML
XSLTTransformations XML complexes
sXMLParsing base sur les flux

Exemples

1. Creer du XML avec iXML

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.
" Factory iXML
lo_ixml = cl_ixml=>create( ).
" Nouveau document
lo_document = lo_ixml->create_document( ).
" Element racine
DATA(lo_root) = lo_document->create_element( name = 'Customers' ).
lo_document->append_child( lo_root ).
" Ajouter un element client
DATA(lo_customer) = lo_document->create_element( name = 'Customer' ).
lo_customer->set_attribute( name = 'id' value = '1000' ).
lo_root->append_child( lo_customer ).
" Elements enfants
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 ).
" Rendre en string
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( ).
" Resultat:
" <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. Parser du XML avec iXML

DATA: lv_xml TYPE xstring.
" XML en entree
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>`
).
" Creer le parser
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.
" Erreur de parsing
DATA(lv_error) = lo_parser->get_error( )->get_reason( ).
WRITE: / 'Erreur de parsing:', lv_error.
RETURN.
ENDIF.
" Naviguer dans les elements
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' ).
" Lire les elements enfants
DATA(lo_name_node) = lo_customer->find_from_name( 'Name' ).
DATA(lo_city_node) = lo_customer->find_from_name( 'City' ).
WRITE: / 'ID:', lv_id,
/ 'Nom:', lo_name_node->get_value( ),
/ 'Ville:', lo_city_node->get_value( ).
SKIP.
ENDIF.
lo_node = lo_iterator->get_next( ).
ENDWHILE.

3. Simple Transformation (ABAP -> XML)

" Creer la Simple Transformation dans SE80:
" Nom: 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>
" Code ABAP
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' )
).
" Appeler la transformation
CALL TRANSFORMATION zcustomer_to_xml
SOURCE customers = lt_customers
RESULT XML lv_xml.
" Afficher le XML en string
DATA(lv_xml_string) = cl_abap_codepage=>convert_from( lv_xml ).
WRITE: / lv_xml_string.

4. Simple Transformation (XML -> ABAP)

" Simple Transformation inverse (ou utiliser la meme)
DATA: lt_customers TYPE STANDARD TABLE OF ty_customer,
lv_xml TYPE xstring.
" XML en entree
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.
" Traiter les donnees
LOOP AT lt_customers INTO DATA(ls_customer).
WRITE: / ls_customer-kunnr, ls_customer-name1, ls_customer-ort01.
ENDLOOP.

5. ID-Transformation (serialisation automatique)

" ID = Identity Transformation (standard)
" Serialise automatiquement les donnees ABAP en asXML
DATA: ls_customer TYPE kna1,
lv_xml TYPE xstring.
ls_customer = VALUE #(
kunnr = '0000001000"
name1 = 'Test Customer"
ort01 = 'Berlin"
land1 = 'DE"
).
" ABAP -> XML (format asXML)
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. Transformation XSLT

" Creer le XSLT dans SE80:
" Nom: ZFORMAT_CUSTOMERS
" <xsl:transform version="1.0"
" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
" <xsl:template match="/">
" <html>
" <body>
" <h1>Liste des clients</h1>
" <table border="1">
" <tr><th>ID</th><th>Nom</th><th>Ville</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.
" XML en entree
lv_xml_in = cl_abap_codepage=>convert_to(
`<Customers>` &&
` <Customer><Id>1000</Id><Name>Mustermann</Name><City>Berlin</City></Customer>` &&
`</Customers>`
).
" Appliquer XSLT
CALL TRANSFORMATION zformat_customers
SOURCE XML lv_xml_in
RESULT XML lv_html.
" Afficher le HTML
DATA(lv_html_string) = cl_abap_codepage=>convert_from( lv_html ).

7. XML avec 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( ).
" Racine avec namespace
DATA(lo_root) = lo_document->create_element_ns(
name = 'Order"
prefix = 'ord"
uri = 'http://example.com/order"
).
lo_document->append_child( lo_root ).
" Declaration de namespace
lo_root->set_attribute_ns(
name = 'xmlns:ord"
value = 'http://example.com/order"
).
" Element dans le 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 ).
" Rendre
DATA(lo_stream) = lo_ixml->create_stream_factory( )->create_ostream_xstring( lv_xml ).
lo_ixml->create_renderer( document = lo_document ostream = lo_stream )->render( ).
" Resultat:
" <ord:Order xmlns:ord="http://example.com/order">
" <ord:Item>Product A</ord:Item>
" </ord:Order>

8. Requetes XPath

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>Muller</Name></Customer>` &&
` <Customer id="1001" country="AT"><Name>Schmidt</Name></Customer>` &&
` <Customer id="1002" country="DE"><Name>Weber</Name></Customer>` &&
`</Customers>`
).
" Parser
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( ).
" Requete XPath: Tous les clients allemands
DATA(lo_xpath) = cl_ixml_xpath=>create( lo_document ).
DATA(lo_result) = lo_xpath->evaluate( '//Customer[@country="DE"]' ).
" Parcourir les resultats
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: / 'Client DE:', lo_name->get_value( ).
lo_node = lo_iter->get_next( ).
ENDWHILE.

9. Classe XML pour un traitement simplifie

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.
" Parser le XML
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.
" Utilisation
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 - Parsing base sur les flux

" Efficace pour les grands fichiers XML
DATA: lv_xml TYPE xstring.
lv_xml = cl_abap_codepage=>convert_to(
`<Items><Item>A</Item><Item>B</Item><Item>C</Item></Items>`
).
" Creer le reader
DATA(lo_reader) = cl_sxml_string_reader=>create( lv_xml ).
" Parcourir le flux
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: / 'Debut element:', lo_open->qname-name.
WHEN if_sxml_node=>co_nt_element_close.
DATA(lo_close) = CAST if_sxml_close_element( lo_node ).
WRITE: / 'Fin element:', lo_close->qname-name.
WHEN if_sxml_node=>co_nt_value.
DATA(lo_value) = CAST if_sxml_value_node( lo_node ).
WRITE: / 'Valeur:', lo_value->get_value( ).
ENDCASE.
ENDDO.
CATCH cx_sxml_parse_error INTO DATA(lx_error).
WRITE: / 'Erreur de parsing:', lx_error->get_text( ).
ENDTRY.

11. Convertir XML et JSON

" 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>`
).
" D'abord vers ABAP, puis vers 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. Validation de schema XML

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>`
).
" Valider
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 est valide'.
ELSE.
WRITE: / 'XML n''est pas valide'.
ENDIF.
CATCH cx_root INTO DATA(lx_error).
WRITE: / 'Erreur:', lx_error->get_text( ).
ENDTRY.

13. Simple Transformation avec conditions

" Simple Transformation avec conditions:
" <?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. Creer une enveloppe SOAP

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( ).
" Enveloppe SOAP
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 ).
" Declarer le namespace
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 ).
" Contenu de la requete
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 ).
" Rendre
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. Lire et traiter un fichier XML

DATA: lv_file TYPE string VALUE '/tmp/customers.xml',
lv_xml TYPE xstring,
lv_content TYPE string.
" Lire le fichier
OPEN DATASET lv_file FOR INPUT IN BINARY MODE.
IF sy-subrc = 0.
READ DATASET lv_file INTO lv_xml.
CLOSE DATASET lv_file.
" Traiter le XML
DATA(lo_xml) = NEW zcl_xml_helper( iv_xml = lv_xml ).
" Extraire les valeurs
DATA(lv_name) = lo_xml->get_value( '//Customer/Name' ).
DATA(lv_city) = lo_xml->get_value( '//Customer/City' ).
WRITE: / 'Nom:', lv_name,
/ 'Ville:', lv_city.
ENDIF.

Remarques importantes / Bonnes pratiques

  • Simple Transformations pour une conversion ABAP <-> XML simple.
  • iXML pour une manipulation DOM complexe.
  • sXML pour les grands fichiers (base sur les flux, moins de memoire).
  • XSLT pour les transformations complexes et la generation HTML.
  • ID-Transformation pour une serialisation rapide (format asXML).
  • Namespaces : declarer et utiliser correctement.
  • Encodage : attention (UTF-8 recommande).
  • XPath : utiliser pour des requetes efficaces.
  • Implementer la gestion des erreurs pour les operations de parsing.
  • Combiner avec le traitement JSON pour les APIs REST.