Application Development Discussions
Join the discussions or start your own on all things application development, including tools and APIs, programming models, and keeping your skills sharp.
cancel
Showing results for 
Search instead for 
Did you mean: 

Transforming ABAP internal table to JSON array (rather than object)

dave_price
Participant
0 Kudos

Is there an easy way to do this using the standard CALL TRANSFORMATION?  Here's what I'm doing:

"ABAP to JSON

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

   CALL TRANSFORMATION id SOURCE ARRAY = lt_table

                          RESULT XML writer.

   lv_json = writer->get_output( ).

Now this generates JSON with an object named ARRAY in the proper format with sub-objects... but I don't want an object named ARRAY, I just want an unnamed array.

I searched through the documentation, the asJSON documentation seems to imply this should be possible, but doesn't specify any way to accomplish such a thing.  Leaving the name blank just generates an error.

Tried a few transformation OPTIONS as well, but none of them seem to accomplish this.

It's not a huge deal to use an object instead (although we're trying to squeeze every microsecond of performance out of this call), or even to just write my own darn transformation, but I guess I'm surprised SAP doesn't seem to support this functionality with CALL TRANSFORMATION.  Am I missing something?  Please tell me I'm missing something

1 ACCEPTED SOLUTION

dave_price
Participant

I honestly can't remember why I asked this question (I think I was just confused about why the object needed a name but that turned out to not really be a problem), but here's what I ended up doing, in case it's helpful:

EDIT: I noticed above someone asked about uppercasing the JSON -- I also had to do this because the REST service I was calling required it.  You can see below I actually convert back to string to do this, though there may be a more elegant solution.

*for call to cl_http_client=>create_by_destination

   constantslc_dest    TYPE rfcdest  value 'MILEMAKER'.

*local structures for handling input/output

   DATA: ls_postal_dist type ZST_POSTAL_DIST,

         ls_postal_conv type ZST_POSTAL_DIST,

         lt_postal_conv type ZTT_POSTAL_DIST,

*for call to cl_http_client=>create_by_destination

         lo_CLIENT type ref to IF_HTTP_CLIENT,

         ls_RANDMCNALLY_JSON type ZST_RANDMCNALLY_JSON,

         lt_RANDMCNALLY_JSON type table of ZST_RANDMCNALLY_JSON,

         lv_HTTP_REASON type string,

         lv_HTTP_CODE type i,

*JSON conversion

         lo_writer TYPE REF TO cl_sxml_string_writer,

         lv_json_in TYPE xstring,

         lv_json_out TYPE xstring,

         lv_json_string type string,

         lo_conv  type ref to CL_ABAP_CONV_IN_CE,

         lo_conv2 type ref to CL_ABAP_CONV_OUT_CE,

*return parameters and error handlers

         lo_cxroot   TYPE REF TO cx_root,

         lv_message type string,

         lv_PAR1 LIKE  SY-MSGV1,

         lv_PAR2 LIKE  SY-MSGV2,

         lv_PAR3 LIKE  SY-MSGV3,

         ls_return type bapiret2"return structure

[snip]

*set request parameters

       CALL METHOD lo_client->request->SET_CONTENT_TYPE('application/json').

       CALL METHOD lo_client->request->SET_METHOD('POST').

*convert data to JSON

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

       CALL TRANSFORMATION id SOURCE routes = lt_RANDMCNALLY_JSON initial_components = 'suppress' RESULT XML lo_writer.

       lv_json_in = lo_writer->get_output( ).

*attach data to body

       CALL METHOD lo_client->request->SET_DATA

         EXPORTING

           data = lv_json_in.

*get JSON text in string format for testing

       CALL METHOD CL_ABAP_CONV_IN_CE=>CREATE

         EXPORTING

           INPUT       = lv_json_in

           ENCODING    = 'UTF-8'

           REPLACEMENT = '?'

           IGNORE_CERR = ABAP_TRUE

         RECEIVING

           CONV        = lo_conv.

       CALL METHOD lo_conv->READ

         IMPORTING

           DATA = lv_json_string.

*send HTTP POST call to RandMcNally webservice

       call method lo_client->send

* exporting  timeout = lc_timeout

        exceptions http_communication_failure  = 1

        http_invalid_state = 2

        http_processing_failed = 3

        others = 4.

       if sy-subrc <> 0.

         CALL METHOD lo_client->get_last_error

           IMPORTING

             message = lv_message.

         CONCATENATE 'HTTP POST:' lv_message into lv_message.

         move lv_message to ls_return-message.

         append ls_return to return.

       endif.

*receive data back

       CALL METHOD lo_client->receive

         EXCEPTIONS

           http_communication_failure = 1

           http_invalid_state         = 2

           http_processing_failed     = 3

           others                     = 4.

*pass errors back, if any

       if sy-subrc <> 0.

         clear ls_return.

         ls_return-type = 'E'.

         CALL METHOD lo_client->get_last_error

           IMPORTING

             message = lv_message.

         move sy-subrc to lv_json_string.

         CONCATENATE 'RECEIVE DATA:' lv_json_string lv_message into ls_return-message SEPARATED BY space.

         append ls_return to return.

       endif.

*get the data (a table in JSON format) from response object

       clear lv_json_out.

       lv_json_out = lo_client->response->GET_DATA( ).

*get the status of the response

       CALL METHOD lo_client->response->GET_STATUS

         IMPORTING

           CODE   = lv_HTTP_CODE

           REASON = lv_HTTP_REASON.

*if response status code not 200 (OK), return error and cease processing

       if lv_http_code <> '200'.

         clear ls_return.

         ls_return-type = 'E'.

         move lv_http_code to lv_json_string.

         CONCATENATE 'GET STATUS:' lv_json_string lv_HTTP_REASON into ls_return-message SEPARATED BY space.

         append ls_return to return.

       ENDIF.

     catch cx_root into lo_cxroot.

     cleanup.

       ls_return-message = lo_cxroot->get_text( ).

       append ls_return to return.

   endtry.

*close channel

   CALL METHOD lo_client->close

     EXCEPTIONS

       http_invalid_state = 1

       others             = 2.

*trying to process after error sometimes results in short dump because lv_json_out contains something other than JSON

   if ls_return is not initial.

     return.

   endif.

   if lv_json_out is not initial.

*convert JSON to string and make it UPPERCASE so that SAP can do transformation

     CALL METHOD CL_ABAP_CONV_IN_CE=>CREATE

       EXPORTING

         INPUT       = lv_json_out

         ENCODING    = 'UTF-8'

         REPLACEMENT = '?'

         IGNORE_CERR = ABAP_TRUE

       RECEIVING

         CONV        = lo_conv.

     CALL METHOD lo_conv->READ

       IMPORTING

         DATA = lv_json_string.

     TRANSLATE lv_json_string to UPPER CASE.

*convert JSON back to xstring

     CALL METHOD CL_ABAP_CONV_OUT_CE=>CREATE

       EXPORTING

         ENCODING    = 'UTF-8'

         REPLACEMENT = '?'

         IGNORE_CERR = ABAP_TRUE

       RECEIVING

         CONV        = lo_conv2.

     lo_conv2->convert( EXPORTING data = lv_json_string

                  IMPORTING buffer = lv_json_out ).

*convert our now UPPERCASE xstring to output table (JSON to ABAP)

     clear lt_randmcnally_json.

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

     CALL TRANSFORMATION id OPTIONS value_handling = 'accept_decimals_loss'

                            SOURCE XML lv_json_out

                            RESULT routes = lt_RANDMCNALLY_JSON.


[snip]

10 REPLIES 10

former_member203179
Participant
0 Kudos

I have the same problem - the service I want to work with has a problem with the result from the default SAP json transformation and don't want the embracing object.

CALL TRANSFORMATION id SOURCE source = it_tab RESULT XML lo_json_writer.

rv_json_data = cl_abap_codepage=>convert_from( lo_json_writer->get_output( ) ).

Result SAP Transformation in rv_json_data :

{
   "SOURCE":[
      {
         "VBELN":"0000000001",
         "POSNR":"000010",
         "VKORG":"1111"
      },
      {
         "VBELN":"0000000001",
         "POSNR":"000020",
         "VKORG":"1111"
      }
   ]
}


Result wanted:

[

   {

      "VBELN":"0000000001",

      "POSNR":"000010",

      "VKORG":"1111"

   },

   {

      "VBELN":"0000000001",

      "POSNR":"000020",

      "VKORG":"1111"

   }

]

Is there any possiblity to exclude the object name and only get an array - or I really need to manipulate the string at the end ?

0 Kudos

You can try this approach:


* serialize info
DATA:
   l_serializer TYPE REF TO cl_trex_json_serializer,
   json_c       TYPE string,
   json_x       TYPE xstring.

CREATE OBJECT l_serializer
   EXPORTING
     data = ls_data.      " your data here!
l_serializer->serialize( ) .
json_c = l_serializer->get_data( ) .


0 Kudos

Hi Kay - many thanks for this hint !

this was a good tip

When I replaced the CL_TREX_JSON_SERIALIZER class with the transformation :


* serialize info

DATA l_serializer TYPE REF TO cl_trex_json_serializer.

CREATE OBJECT l_serializer

  EXPORTING data = it_tab.

l_serializer->serialize( ) .

rv_json_data = l_serializer->get_data( ) .

I get the needed JSON structure in rv_json_data - but now I have the problem that this serializer don't put the attribute in double quotes which is needed to be valid JSON :

Result SAP CL_TREX_JSON_SERIALIZER Transformation in rv_json_data :

[

   {

      vbeln:"0000000001",

      posnr:"000010",

      vkorg:"1111"

   },

   {

      vbeln:"0000000001",

      posnr:"000020",

      vkorg:"1111"

   }

]

Result wanted:

[

   {

      "vbeln":"0000000001",

      "posnr":"000010",

      "vkorg":"1111"

   },

   {

      "vbeln":"0000000001",

      "posnr":"000020",

      "vkorg":"1111"

   }

]

And I would prefer to also uppercase the attributes - can I accomplish this somehow ???

Best,

Thomas

Edit: I found out that this is a well known problem ->

dave_price
Participant

I honestly can't remember why I asked this question (I think I was just confused about why the object needed a name but that turned out to not really be a problem), but here's what I ended up doing, in case it's helpful:

EDIT: I noticed above someone asked about uppercasing the JSON -- I also had to do this because the REST service I was calling required it.  You can see below I actually convert back to string to do this, though there may be a more elegant solution.

*for call to cl_http_client=>create_by_destination

   constantslc_dest    TYPE rfcdest  value 'MILEMAKER'.

*local structures for handling input/output

   DATA: ls_postal_dist type ZST_POSTAL_DIST,

         ls_postal_conv type ZST_POSTAL_DIST,

         lt_postal_conv type ZTT_POSTAL_DIST,

*for call to cl_http_client=>create_by_destination

         lo_CLIENT type ref to IF_HTTP_CLIENT,

         ls_RANDMCNALLY_JSON type ZST_RANDMCNALLY_JSON,

         lt_RANDMCNALLY_JSON type table of ZST_RANDMCNALLY_JSON,

         lv_HTTP_REASON type string,

         lv_HTTP_CODE type i,

*JSON conversion

         lo_writer TYPE REF TO cl_sxml_string_writer,

         lv_json_in TYPE xstring,

         lv_json_out TYPE xstring,

         lv_json_string type string,

         lo_conv  type ref to CL_ABAP_CONV_IN_CE,

         lo_conv2 type ref to CL_ABAP_CONV_OUT_CE,

*return parameters and error handlers

         lo_cxroot   TYPE REF TO cx_root,

         lv_message type string,

         lv_PAR1 LIKE  SY-MSGV1,

         lv_PAR2 LIKE  SY-MSGV2,

         lv_PAR3 LIKE  SY-MSGV3,

         ls_return type bapiret2"return structure

[snip]

*set request parameters

       CALL METHOD lo_client->request->SET_CONTENT_TYPE('application/json').

       CALL METHOD lo_client->request->SET_METHOD('POST').

*convert data to JSON

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

       CALL TRANSFORMATION id SOURCE routes = lt_RANDMCNALLY_JSON initial_components = 'suppress' RESULT XML lo_writer.

       lv_json_in = lo_writer->get_output( ).

*attach data to body

       CALL METHOD lo_client->request->SET_DATA

         EXPORTING

           data = lv_json_in.

*get JSON text in string format for testing

       CALL METHOD CL_ABAP_CONV_IN_CE=>CREATE

         EXPORTING

           INPUT       = lv_json_in

           ENCODING    = 'UTF-8'

           REPLACEMENT = '?'

           IGNORE_CERR = ABAP_TRUE

         RECEIVING

           CONV        = lo_conv.

       CALL METHOD lo_conv->READ

         IMPORTING

           DATA = lv_json_string.

*send HTTP POST call to RandMcNally webservice

       call method lo_client->send

* exporting  timeout = lc_timeout

        exceptions http_communication_failure  = 1

        http_invalid_state = 2

        http_processing_failed = 3

        others = 4.

       if sy-subrc <> 0.

         CALL METHOD lo_client->get_last_error

           IMPORTING

             message = lv_message.

         CONCATENATE 'HTTP POST:' lv_message into lv_message.

         move lv_message to ls_return-message.

         append ls_return to return.

       endif.

*receive data back

       CALL METHOD lo_client->receive

         EXCEPTIONS

           http_communication_failure = 1

           http_invalid_state         = 2

           http_processing_failed     = 3

           others                     = 4.

*pass errors back, if any

       if sy-subrc <> 0.

         clear ls_return.

         ls_return-type = 'E'.

         CALL METHOD lo_client->get_last_error

           IMPORTING

             message = lv_message.

         move sy-subrc to lv_json_string.

         CONCATENATE 'RECEIVE DATA:' lv_json_string lv_message into ls_return-message SEPARATED BY space.

         append ls_return to return.

       endif.

*get the data (a table in JSON format) from response object

       clear lv_json_out.

       lv_json_out = lo_client->response->GET_DATA( ).

*get the status of the response

       CALL METHOD lo_client->response->GET_STATUS

         IMPORTING

           CODE   = lv_HTTP_CODE

           REASON = lv_HTTP_REASON.

*if response status code not 200 (OK), return error and cease processing

       if lv_http_code <> '200'.

         clear ls_return.

         ls_return-type = 'E'.

         move lv_http_code to lv_json_string.

         CONCATENATE 'GET STATUS:' lv_json_string lv_HTTP_REASON into ls_return-message SEPARATED BY space.

         append ls_return to return.

       ENDIF.

     catch cx_root into lo_cxroot.

     cleanup.

       ls_return-message = lo_cxroot->get_text( ).

       append ls_return to return.

   endtry.

*close channel

   CALL METHOD lo_client->close

     EXCEPTIONS

       http_invalid_state = 1

       others             = 2.

*trying to process after error sometimes results in short dump because lv_json_out contains something other than JSON

   if ls_return is not initial.

     return.

   endif.

   if lv_json_out is not initial.

*convert JSON to string and make it UPPERCASE so that SAP can do transformation

     CALL METHOD CL_ABAP_CONV_IN_CE=>CREATE

       EXPORTING

         INPUT       = lv_json_out

         ENCODING    = 'UTF-8'

         REPLACEMENT = '?'

         IGNORE_CERR = ABAP_TRUE

       RECEIVING

         CONV        = lo_conv.

     CALL METHOD lo_conv->READ

       IMPORTING

         DATA = lv_json_string.

     TRANSLATE lv_json_string to UPPER CASE.

*convert JSON back to xstring

     CALL METHOD CL_ABAP_CONV_OUT_CE=>CREATE

       EXPORTING

         ENCODING    = 'UTF-8'

         REPLACEMENT = '?'

         IGNORE_CERR = ABAP_TRUE

       RECEIVING

         CONV        = lo_conv2.

     lo_conv2->convert( EXPORTING data = lv_json_string

                  IMPORTING buffer = lv_json_out ).

*convert our now UPPERCASE xstring to output table (JSON to ABAP)

     clear lt_randmcnally_json.

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

     CALL TRANSFORMATION id OPTIONS value_handling = 'accept_decimals_loss'

                            SOURCE XML lv_json_out

                            RESULT routes = lt_RANDMCNALLY_JSON.


[snip]

0 Kudos

Hi Dave

Thanks for your example.

you used a second content for your transformation:

CALL TRANSFORMATION id SOURCE routes = lt_RANDMCNALLY_JSON initial_components = 'suppress' RESULT XML lo_writer.

which ends up in a JSON like this:

{

   "ROUTES":[

      {

         "VBELNPOSNR_ID":"0000007461000010",

         "VBELN":"0000000001",

         "POSNR":"000010",

         "VKORG":"1111"

      }

   ],

   "INITIAL_COMPONENTS":"suppress"

}

And I think you managed the rest in the other system, that they should ignore the object "ROUTES" when the attribute "INITIAL_COMPONENTS":"suppress" is set.

0 Kudos

Hi Thomas, yes our output was this:

{"ROUTES":[{"ORIGIN":{"CITY":"","STATE":"","ZIPCODE":"92880"},"DESTINATION":{"CITY":"","STATE":"","ZIPCODE":"90058"},"MILEAGE":0}],"INITIAL_COMPONENTS":"suppress"}

The REST webservice then ignores ROUTES.

horst_keller
Product and Topic Expert
Product and Topic Expert
0 Kudos

You can write STs to achieve arrays from internal tables, there is an example in the documentation.

Former Member
0 Kudos

Hello,

Is there a way that I can the opposite, I get a JSON from somewhere with a service and now I want to transform the data to an ABAP internal table so I can save it to the database

Thanks, Dren

0 Kudos

Hi Dren,

I used the SAP class CL_TREX_JSON_DESERIALIZER for that  :


data lr_json_deserializer type ref to CL_TREX_JSON_DESERIALIZER.

create object lr_json_deserializer.

lr_json_deserializer->deserialize(

   exporting

     json  = iv_json

   importing

     abap  = e_tab ).

but because I adapted the CL_TREX_JSON_SERIALIZER

I also adapted the CL_TREX_JSON_DESERIALIZER for handling data like:

    "vbeln":"0000000001",

instead of

    vbeln:"0000000001",



For that I changed in my deserializer class the DESERIALIZE_OBJECT method:


0 Kudos

Yep, I did that in my code above (my RFC converts ABAP to JSON and then JSON to ABAP).  I'll repost that section here.  For some reason, ABAP doesn't like lowercase JSON so I had to convert it to uppercase.

You could also use the deserializer I suppose, but I think the transformation is a lot simpler. YMMV.

*convert JSON to string and make it UPPERCASE so that SAP can do transformation

     CALL METHOD CL_ABAP_CONV_IN_CE=>CREATE

       EXPORTING

         INPUT       = lv_json_out

         ENCODING    = 'UTF-8'

         REPLACEMENT = '?'

         IGNORE_CERR = ABAP_TRUE

       RECEIVING

         CONV        = lo_conv.

     CALL METHOD lo_conv->READ

       IMPORTING

         DATA = lv_json_string.

     TRANSLATE lv_json_string to UPPER CASE.

*convert JSON back to xstring

     CALL METHOD CL_ABAP_CONV_OUT_CE=>CREATE

       EXPORTING

         ENCODING    = 'UTF-8'

         REPLACEMENT = '?'

         IGNORE_CERR = ABAP_TRUE

       RECEIVING

         CONV        = lo_conv2.

     lo_conv2->convert( EXPORTING data = lv_json_string

                  IMPORTING buffer = lv_json_out ).

*convert our now UPPERCASE xstring to output table (JSON to ABAP)

     clear lt_randmcnally_json.

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

     CALL TRANSFORMATION id OPTIONS value_handling = 'accept_decimals_loss'

                            SOURCE XML lv_json_out

                            RESULT routes = lt_RANDMCNALLY_JSON.