[ABAP] XML-Strings parsen

Variante 1 (IF_IXML_ELEMENT, IF_IXML_NODE_COLLECTION)

* XML-Daten
DATA(lv_xml) = |<object>| &&
               |  <str name="text">abcd</str>| &&
               |  <bool name="flag">true</bool>| &&
               |  <member name="number"><num>111</num></member>| &&
               |  <member name="content"><null /></member>| &&
               |  <member name="attr"></member>| &&
               |</object>|.

* XML-Interface
DATA(o_ixml) = cl_ixml=>create( ).
* XML-Doc
DATA(o_doc) = o_ixml->create_document( ).
* Stream-Factory
DATA(o_sf) = o_ixml->create_stream_factory( ).
* Stream
DATA(o_stream) = o_sf->create_istream_string( string = lv_xml ).

* Parser-Objekt erzeugen
DATA(o_parser) = o_ixml->create_parser( document       = o_doc
                                        istream        = o_stream
                                        stream_factory = o_sf ).

* XML parsen
IF o_parser->parse( ) = 0.
* Root-Node
  DATA(o_root) = o_doc->get_root_element( ).

* Alle Nodes mit Namen 'member'
  DATA(o_nodes_member) = o_root->get_elements_by_tag_name( name = 'member' ).
* Iterator für die gefundenen Nodes
  DATA(o_node_iterator_members) = o_nodes_member->create_iterator( ).
* ersten Iterator-Wert holen
  DATA(o_nodes_temp) = o_node_iterator_members->get_next( ).

* Iterator durchgehen
  WHILE NOT o_nodes_temp IS INITIAL.
* Wert der Node ausgeben
    WRITE: / o_nodes_temp->get_value( ).

* Attribute
    DATA(o_note_temp_attr) = o_nodes_temp->get_attributes( ).
    DATA(o_node_temp_item) = o_note_temp_attr->get_named_item( 'name' ).
    IF o_node_temp_item IS BOUND.
      WRITE: / o_node_temp_item->get_value( ).
    ENDIF.

* Children
    DATA(o_node_temp_children) = o_nodes_temp->get_children( ).
    WRITE: / 'Childrens:', o_node_temp_children->get_length( ).

* nächster Iterator-Wert
    o_nodes_temp = o_node_iterator_members->get_next( ).
  ENDWHILE.
ELSE.
* Fehlerauswertung
  DO o_parser->num_errors( ) TIMES.
    DATA(o_err) = o_parser->get_error( index = sy-index - 1 ).
    IF o_err IS BOUND.
      WRITE: / o_err->get_column( ), o_err->get_line( ), o_err->get_reason( ).
    ENDIF.
  ENDDO.
ENDIF.

Variante 2 (find_from_name)

* XML-Daten
DATA(lv_xml) = |<object>| &&
               |  <str name="text">abcd</str>| &&
               |  <bool name="flag">true</bool>| &&
               |  <member name="number"><num>111</num></member>| &&
               |  <member name="content"><null /></member>| &&
               |  <member name="attr"></member>| &&
               |</object>|.

* XML-Interface
DATA(o_ixml) = cl_ixml=>create( ).
* XML-Doc
DATA(o_doc) = o_ixml->create_document( ).
* Stream-Factory
DATA(o_sf) = o_ixml->create_stream_factory( ).
* Stream
DATA(o_stream) = o_sf->create_istream_string( string = lv_xml ).

* Parser-Objekt erzeugen
DATA(o_parser) = o_ixml->create_parser( document       = o_doc
                                        istream        = o_stream
                                        stream_factory = o_sf ).

* XML parsen
IF o_parser->parse( ) = 0.
* <object>
  DATA(o_root) = o_doc->find_from_name( 'object' ).

  IF o_root IS BOUND.
* <str ...>
    DATA(o_str) = o_root->get_first_child( ).
    IF o_str IS BOUND.
* Wert der Node ausgeben
      WRITE: / o_str->get_value( ).

* Attribut 'name'
      DATA(o_attr) = o_str->get_attributes( ).
      DATA(o_name) = o_attr->get_named_item( 'name' ).
      IF o_name IS BOUND.
        WRITE: / o_name->get_value( ).
      ENDIF.
    ENDIF.
  ENDIF.
ELSE.
* Fehlerauswertung
  DO o_parser->num_errors( ) TIMES.
    DATA(o_err) = o_parser->get_error( index = sy-index - 1 ).
    IF o_err IS BOUND.
      WRITE: / o_err->get_column( ), o_err->get_line( ), o_err->get_reason( ).
    ENDIF.
  ENDDO.
ENDIF.

[ABAP] JSON parsen und in Node-Table wandeln

* Quelle: https://github.com/i042416/KnowlegeRepository/blob/master/ABAP/SmallApp/011_ZCL_JERRY_TOOL.abap
CLASS lcl_json_parser DEFINITION.
  PUBLIC SECTION.

    TYPES: BEGIN OF ty_node,
             type      TYPE string,
             prefix    TYPE string,
             name      TYPE string,
             nsuri     TYPE string,
             value     TYPE string,
             value_raw TYPE xstring,
           END OF ty_node.

    TYPES: ty_it_node TYPE STANDARD TABLE OF ty_node WITH DEFAULT KEY.

    CONSTANTS: co_open_element TYPE string VALUE 'OPEN'.
    CONSTANTS: co_close_element TYPE string VALUE 'CLOSE'.
    CONSTANTS: co_attribute TYPE string VALUE 'ATTRIBUTE'.
    CONSTANTS: co_value TYPE string VALUE 'VALUE'.

    CLASS-METHODS: convert_json_to_node_table
      IMPORTING
                iv_json            TYPE string
      RETURNING VALUE(rv_it_nodes) TYPE ty_it_node
      RAISING
                cx_sxml_parse_error.
ENDCLASS.

CLASS lcl_json_parser IMPLEMENTATION.
  METHOD convert_json_to_node_table.

* JSON nach XSTRING (UTF-8) konvertieren
    DATA(o_json) = cl_abap_codepage=>convert_to( iv_json ).
* XSTRING einlesen und nach XML parsen
    DATA(o_reader) = cl_sxml_string_reader=>create( o_json ).

    TRY.
* erste Node holen
        DATA(o_node) = o_reader->read_next_node( ).

* solange es Nodes gibt
        WHILE o_node IS BOUND.
* Node-Typen prüfen
          CASE o_node->type.
* Öffnen-Node
            WHEN if_sxml_node=>co_nt_element_open.
              DATA(op) = CAST if_sxml_open_element( o_node ).
              APPEND VALUE #( type   = co_open_element
                              prefix = op->prefix
                              name   = op->qname-name
                              nsuri  = op->qname-namespace ) TO rv_it_nodes.

* Attribute
              LOOP AT op->get_attributes( ) ASSIGNING FIELD-SYMBOL(<a>).
                APPEND VALUE #( type      = co_attribute
                                prefix    = <a>->prefix
                                name      = <a>->qname-name
                                nsuri     = <a>->qname-namespace
                                value     = COND #( WHEN <a>->value_type = if_sxml_value=>co_vt_text THEN <a>->get_value( ) )
                                value_raw = COND #( WHEN <a>->value_type = if_sxml_value=>co_vt_raw THEN <a>->get_value_raw( ) ) ) TO rv_it_nodes.
              ENDLOOP.
* Schließen-Node
            WHEN if_sxml_node=>co_nt_element_close.
              DATA(cl) = CAST if_sxml_close_element( o_node ).
              APPEND VALUE #( type   = co_close_element
                              prefix = cl->prefix
                              name   = cl->qname-name
                              nsuri  = cl->qname-namespace ) TO rv_it_nodes.

* Wert-Node
            WHEN if_sxml_node=>co_nt_value.
              DATA(val) = CAST if_sxml_value_node( o_node ).
              APPEND VALUE #( type      = co_value
                              value     = COND #( WHEN val->value_type = if_sxml_value=>co_vt_text THEN val->get_value( ) )
                              value_raw = COND #( WHEN val->value_type = if_sxml_value=>co_vt_raw THEN val->get_value_raw( ) ) ) TO rv_it_nodes.
* Andere Nodetypen
            WHEN OTHERS.

          ENDCASE.

* nächste Node
          o_node = o_reader->read_next_node( ).
        ENDWHILE.

      CATCH cx_root INTO DATA(e_txt).
        RAISE EXCEPTION TYPE cx_sxml_parse_error
          EXPORTING
            error_text = e_txt->get_text( ).
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  TRY.
* JSON-Beispiele
*      DATA(lv_json) = CONV string( '{"success":"true","msg":"Ok.","matnr":"0000001234"}' ).
      DATA(lv_json) = CONV string( '{"VALUES":[{"NAME":"Horst","TITLE":"Herr","AGE":30},{"NAME":"Jutta","TITLE":"Frau","AGE":35},{"NAME":"Ingo","TITLE":"Herr","AGE":31}]}' ).

* JSON -> Nodetable
      DATA(it_node_values) = lcl_json_parser=>convert_json_to_node_table( lv_json ).

* Datenausgabe
      cl_demo_output=>write_data( lv_json ).
      cl_demo_output=>write_data( it_node_values ).
      cl_demo_output=>display( ).
    CATCH cx_root INTO DATA(e_txt).
      WRITE: / e_txt->get_text( ).
  ENDTRY.

[ABAP] DOM: Hilfs-Klasse zur Ausgabe von XML-Daten

Verwendung

DATA: o_xml_doc TYPE REF TO if_ixml_document.

* Erzeugt String aus DOM-XML-Document
DATA(lv_xml_string) = lcl_xml_tool=>get_xml_string_from_xml_doc( i_dom_xml_doc = o_xml_doc ).

* Gibt rekursiv alle Nodes unterhalb übergebener Node aus
lcl_xml_tool=>print_xml( o_xml_doc->get_root_element( ) ).

Klasse

CLASS lcl_xml_tool DEFINITION.
  PUBLIC SECTION.
  *--------------------------------------------------------------------*
    CLASS-METHODS: get_xml_string_from_xml_doc
      IMPORTING
                i_dom_xml_doc      TYPE REF TO if_ixml_document
                i_encoding         TYPE string DEFAULT 'UTF-8'
      RETURNING VALUE(ret_xml_str) TYPE string.
*--------------------------------------------------------------------*
    CLASS-METHODS: get_node_string
      IMPORTING
                i_dom_xml_node    TYPE REF TO if_ixml_node
                i_close           TYPE boolean
      RETURNING VALUE(ret_string) TYPE string.
*--------------------------------------------------------------------*
    CLASS-METHODS: print_xml
      IMPORTING
        i_dom_xml_root TYPE REF TO if_ixml_node.
*--------------------------------------------------------------------*
  PRIVATE SECTION.
    CONSTANTS: co_stretch_factor TYPE i VALUE 4.
ENDCLASS.
*--------------------------------------------------------------------*
CLASS lcl_xml_tool IMPLEMENTATION.
*--------------------------------------------------------------------*
* Erzeugt String aus DOM-XML-Document
*--------------------------------------------------------------------*
* -> i_dom_xml_doc - DOM-XML-Document
* -> i_encoding    - XML-Encoding
* <- ret_xml_str   - DOM-XML-Document als String
*--------------------------------------------------------------------*
  METHOD get_xml_string_from_xml_doc.

    ret_xml_str = ''.

    TRY.
        DATA(o_ixml) = cl_ixml=>create( ).
        DATA(o_sf) = o_ixml->create_stream_factory( ).
        DATA(o_encoding) = o_ixml->create_encoding( character_set = i_encoding
                                                    byte_order = if_ixml_encoding=>co_none ).

        DATA: lv_xml TYPE string.

        DATA(o_ostream) = o_sf->create_ostream_cstring( lv_xml ).
        o_ostream->set_encoding( encoding = o_encoding ).
        o_ostream->set_pretty_print( pretty_print = abap_true ).

        DATA(o_render) = o_ixml->create_renderer( ostream  = o_ostream
                                                  document = i_dom_xml_doc ).

* XML-String in lv_xml generieren
        DATA(lv_rc) = o_render->render( ).

* Dateigröße in Bytes
        DATA(lv_size) = o_ostream->get_num_written_raw( ).

* Stream schließen
        o_ostream->close( ).

        IF lv_rc = 0 AND lv_size > 0.
          ret_xml_str = lv_xml.
        ENDIF.
      CATCH cx_root.
    ENDTRY.

  ENDMETHOD.
*--------------------------------------------------------------------*
* Erzeugt String aus Node
*--------------------------------------------------------------------*
* -> i_dom_xml_node - Node
* -> i_close        - evtl. Schließen-Tag ausgeben
* <- ret_string     - Node-String
*--------------------------------------------------------------------*
  METHOD get_node_string.

    ret_string = ''.

    TRY.
        CASE i_dom_xml_node->get_type( ).
* Element
          WHEN if_ixml_node=>co_node_element.
            DATA(o_attr) = i_dom_xml_node->get_attributes( ).

            DATA: lv_attr TYPE string.

            IF o_attr IS BOUND.
              DATA(lv_attr_cnt) = o_attr->get_length( ).

* alle Attribute ausgeben
              DO lv_attr_cnt TIMES.
                DATA(o_attri_item) = o_attr->get_item( sy-index - 1 ).

* Namespace
                DATA(lv_namespace) = o_attri_item->get_namespace( ).
*                DATA(lv_prefix) = o_attri_item->get_namespace_prefix( ).

                DATA(lv_ns_attr) = lv_namespace.

* Namespace:Attribut
                IF NOT lv_ns_attr IS INITIAL.
                  lv_ns_attr = |{ lv_ns_attr }:{ o_attri_item->get_name( ) }|.
                else.
                  lv_ns_attr = |{ o_attri_item->get_name( ) }|.
                ENDIF.

                IF lv_attr IS INITIAL.
                  lv_attr = |{ lv_ns_attr }="{ o_attri_item->get_value( ) }"|.
                ELSE.
                  lv_attr = |{ lv_attr } { lv_ns_attr }="{ o_attri_item->get_value( ) }"|.
                ENDIF.
              ENDDO.
            ENDIF.

            IF i_close = abap_false.
              DATA(lv_close_tag) = |>|.
* Gibt es Unterelemente zur Node?
              IF i_dom_xml_node->get_first_child( ) IS INITIAL.
                lv_close_tag = |/>|.
              ENDIF.

              IF strlen( lv_attr ) = 0.
                ret_string = |<{ i_dom_xml_node->get_name( ) }{ lv_close_tag }|.
              ELSE.
                ret_string = |<{ i_dom_xml_node->get_name( ) } { lv_attr }{ lv_close_tag }|.
              ENDIF.
            ELSE.
              ret_string = |</{ i_dom_xml_node->get_name( ) }>|.
            ENDIF.
* Werte
          WHEN if_ixml_node=>co_node_text OR if_ixml_node=>co_node_cdata_section.
            ret_string = i_dom_xml_node->get_value( ).
        ENDCASE.
      CATCH cx_root.
    ENDTRY.

  ENDMETHOD.
*--------------------------------------------------------------------*
* Gibt rekursiv alle Nodes ab i_xml_root (Root) aus
*--------------------------------------------------------------------*
* -> i_dom_xml_root - Root-Node
*--------------------------------------------------------------------*
  METHOD print_xml.
* Einrückung
    DATA(lv_indent) = i_dom_xml_root->get_height( ) * co_stretch_factor.

    WRITE: / |{ ' ' WIDTH = lv_indent }{ get_node_string( i_dom_xml_node = i_dom_xml_root
                                                          i_close        = abap_false ) }|.

    DATA(o_node) = i_dom_xml_root->get_first_child( ).

    IF NOT o_node IS INITIAL.
* Wenn Childs vorhanden -> auflisten
      WHILE NOT o_node IS INITIAL.
        print_xml( o_node ).
        o_node = o_node->get_next( ).
      ENDWHILE.
* evtl. Schließ-Tag setzen
      WRITE: / |{ ' ' WIDTH = lv_indent }{ get_node_string( i_dom_xml_node = i_dom_xml_root
                                                            i_close        = abap_true ) }|.
    ENDIF.

  ENDMETHOD.