Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
horst_keller
Product and Topic Expert
Product and Topic Expert

From time to time there are questions how to transform XML data to JSON data. If you search the Web you find quite a bunch of such converters, e.g.,



In those you paste some XML data and get them back as JSON data. The results of these converters are similar. Especially they follow the same conventions regarding which parts of the XML data are converted to JSON objects and which are converted to JSON arrays.


But is this possible in ABAP too? Are there general classes or function modules for that? I don't know and I didn't look. Instead I accepted the challenge to write a prototype myself.


Creating such a converter with the help of ABAP might be done in several ways:

  • As an XSLT enthusiast, you use transaction STRANS in order to create an XSLT program that transforms the XML data into valid JSON-XML and feed that into a JSON writer of the sXML library.

  • Another idea could be to write an XSLT or Simple-Transformation that serializes the XML data into into appropriate ABAP data and then you use another XSLT, Simple-Transformation, or ID that serializes these data into valid JSON-XML that you feed into a JSON writer


  • You can use the iXML or sXML libraries to parse the XML data and render the ABAP results into valid JSON-XML.

As an old ABAPer I use the parsing/rendering way by employing the token based parsing and rendering offered by the methods of the sXML library. Here is my code:


    TYPES: BEGIN OF node,

             node_type TYPE if_sxml_node=>node_type,

             name      TYPE string,

             value     TYPE string,

             array     TYPE c LENGTH 1,

           END OF node.

    DATA nodes TYPE TABLE OF node WITH EMPTY KEY.

    DATA(xml_string) = `<root />`.

    DO.

      cl_demo_text=>edit_string(

        EXPORTING

          title = 'XML-Data'

        CHANGING

          text_string = xml_string

        EXCEPTIONS

          canceled    = 4 ).

      IF sy-subrc = 4.

        EXIT.

      ENDIF.

      DATA(xml) = cl_abap_codepage=>convert_to(

        replace( val = xml_string sub = |\n| with = `` occ = 0  ) ).

      DATA(out) = cl_demo_output=>new(

        )->begin_section( `XML-Data`

        )->write_xml( xml ).

      "Parsing XML into an internal table

      DATA(reader) = cl_sxml_string_reader=>create( xml ).

      CLEAR nodes.

      TRY.

          DO.

            reader->next_node( ).

            IF reader->node_type = if_sxml_node=>co_nt_final.

              EXIT.

            ENDIF.

            APPEND VALUE #(

              node_type  = reader->node_type

              name       = reader->prefix &&

                           COND string(

                             WHEN reader->prefix IS NOT INITIAL

                                  THEN `:` ) && reader->name

              value      = reader->value ) TO nodes.

            IF reader->node_type = if_sxml_node=>co_nt_element_open.

              DO.

                reader->next_attribute( ).

                IF reader->node_type <> if_sxml_node=>co_nt_attribute.

                  EXIT.

                ENDIF.

                APPEND VALUE #(

                  node_type = if_sxml_node=>co_nt_initial

                  name       = reader->prefix &&

                               COND string(

                                 WHEN reader->prefix IS NOT INITIAL

                                   THEN `:` ) && reader->name

                  value      = reader->value ) TO nodes.

              ENDDO.

            ENDIF.

          ENDDO.

        CATCH cx_sxml_parse_error INTO DATA(parse_error).

          out->write_text( parse_error->get_text( ) ).

      ENDTRY.

      "Determine the array limits in the internal table

      LOOP AT nodes ASSIGNING FIELD-SYMBOL(<node_open>)

                    WHERE

                     node_type = if_sxml_node=>co_nt_element_open

                     AND array IS INITIAL.

        DATA(idx_open) = sy-tabix.

        LOOP AT nodes ASSIGNING FIELD-SYMBOL(<node_close>)

                      FROM idx_open  + 1

                      WHERE

                        node_type = if_sxml_node=>co_nt_element_close

                        AND name = <node_open>-name.

          DATA(idx_close) = sy-tabix.

          IF idx_close < lines( nodes ).

            ASSIGN nodes[ idx_close + 1 ] TO FIELD-SYMBOL(<node>).

            IF <node>-node_type = if_sxml_node=>co_nt_element_open AND

               <node>-name = <node_open>-name.

              <node_open>-array = 'O'.

              <node>-array = '_'.

            ELSEIF

              ( <node>-node_type = if_sxml_node=>co_nt_element_open

                AND <node>-name <> <node_open>-name )

              OR <node>-node_type = if_sxml_node=>co_nt_element_close.

              <node_close>-array = COND #(

                WHEN <node_open>-array = 'O' THEN 'C' ).

              EXIT.

            ENDIF.

          ENDIF.

        ENDLOOP.

      ENDLOOP.

      "Render the internal table to JSON-XML

      DATA(writer) = CAST if_sxml_writer(

       cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ) ).

                             "create( type = if_sxml=>co_xt_xml10 ) ).

      TRY.

          writer->open_element( name = 'object' ).

          LOOP AT nodes ASSIGNING <node>.

            CASE <node>-node_type.

              WHEN if_sxml_node=>co_nt_element_open.

                IF <node>-array IS INITIAL.

                  writer->open_element( name = 'object' ).

                  writer->write_attribute( name = 'name'

                                           value = <node>-name ).

                ELSEIF <node>-array = 'O'.

                  writer->open_element( name = 'array' ).

                  writer->write_attribute( name = 'name'

                                           value = <node>-name ).

                  writer->open_element( name = 'object' ).

                ELSEIF <node>-array = '_'.

                  writer->open_element( name = 'object' ).

                ENDIF.

              WHEN if_sxml_node=>co_nt_element_close.

                IF <node>-array <> 'C'.

                  writer->close_element( ).

                ELSE.

                  writer->close_element( ).

                  writer->close_element( ).

                ENDIF.

              WHEN if_sxml_node=>co_nt_initial.

                writer->open_element( name = 'str' ).

                writer->write_attribute( name = 'name'

                                         value = `a_` && <node>-name ).

                writer->write_value( <node>-value ).

                writer->close_element( ).

              WHEN if_sxml_node=>co_nt_value.

                writer->open_element( name = 'str' ).

                writer->write_attribute( name = 'name'

                                         value = `e_` && <node>-name ).

                writer->write_value( <node>-value ).

                writer->close_element( ).

              WHEN OTHERS.

                out->display( 'A node type is not yet supported' ).

                RETURN.

            ENDCASE.

          ENDLOOP.

          writer->close_element( ).

          DATA(json) =

            CAST cl_sxml_string_writer( writer )->get_output( ).

          out->next_section( 'JSON-Data' ).

          IF writer->if_sxml~type = if_sxml=>co_xt_json.

            out->write_json( json ).

          ELSEIF writer->if_sxml~type = if_sxml=>co_xt_xml10.

            out->write_xml( json ).

          ENDIF.

        CATCH cx_sxml_error INTO DATA(exc).

          out->write( exc->get_text( ) ).

      ENDTRY.

      out->display( ).

    ENDDO.

This example allows you to enter some valid XML into a text edit control and tries to convert it to JSON.


  • Since I have to find the limits of arrays, I cannot parse and render in one go. Therefore I parse the XML data into an internal table nodes first. This part I have mainly taken from my existing sXML parsing example.

  • Then I determine the array limits in the internal table (my poor man's DOM) for which I have added an own column array. This is the clumsy part of the example. Feel invited to propose a better way!

  • Finally, I render the contents of the internal table to JSON-XML. This is straightforward. Note that I distinguish values that stem from XML attributes and those that come from the XML elements itself with prefixes e_ and a_.


And that's that.


If you enter XML data, e.g. as follows:


<order number="4711" xmlns:demo="http://www.sap.com/abapdemos">

<demo:head>

  <demo:status>confirmed</demo:status>

  <demo:date format="mm‑dd‑yyyy">07‑19‑2012</demo:date>

</demo:head>

<demo:body>

  <demo:item units="2" price="17.00">Part No. 0110</demo:item>

  <demo:item units="1" price="10.50">Part No. 1609</demo:item>

  <demo:item units="5" price="12.30">Part No. 1710</demo:item>

</demo:body>

</order>


You receive the following JSON data.



{

"order":

{

  "a_number":"4711",

  "demo:head":

  {

   "demo:status":

   {

    "e_demo:status":"confirmed"

   },

   "demo:date":

   {

    "a_format":"mm‑dd‑yyyy",

    "e_demo:date":"07‑19‑2012"

   }

  },

  "demo:body":

  {

   "demo:item":

   [

    {

     "a_units":"2",

     "a_price":"17.00",

     "e_demo:item":"Part No. 0110"

    },

    {

     "a_units":"1",

     "a_price":"10.50",

     "e_demo:item":"Part No. 1609"

    },

    {

     "a_units":"5",

     "a_price":"12.30",

     "e_demo:item":"Part No. 1710"

    }

   ]

  }

}

}


The result is at least similar to those of the above mentioned online converters, yay!


If you create an XML writer instead of the JSON writer in the above code with create( type = if_sxml=>co_xt_xml10 ), you receive and display the JSON-XML.


The program is far from being bullet-proof. Just an example, tested with some harmless XML data. I restricted the conversion to textual data (no numbers in JSON, no handling of raws) and other things might be missing to. Be invited to find errors and gaps and propose solutions. Maybe there is even an official tool available?


Cheers!


Horst


PS:


Be aware, that there is not one standard way of converting arbitrary XML data to JSON data. Or is there?


The conversions shown here are based on some conventions. In real life examples the desired results might look different and depend on the semantical data structure based on XSD, XML schema or whatsoever. I would expect that in most (business) use cases transformations from XML to JSON are rather specific than general.




13 Comments