05-08-2014 8:58 PM
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
12-03-2014 4:11 PM
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
constants: lc_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]
12-02-2014 5:15 PM
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 :
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 ?
12-03-2014 8:47 AM
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( ) .
12-03-2014 9:45 AM
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
12-03-2014 4:11 PM
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
constants: lc_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]
12-03-2014 4:47 PM
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:
"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.
12-03-2014 8:48 PM
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.
02-18-2015 10:14 AM
You can write STs to achieve arrays from internal tables, there is an example in the documentation.
03-16-2015 3:25 PM
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
03-16-2015 3:43 PM
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:
03-16-2015 3:46 PM
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.