You may (should) have read part I of this blog post already to understand the motivation why I haven't used SAP Gateway for this short example.
In part I you'll also find some information / coding I'm not going to repeat here.
The Motivation
The other day one of my customers upgraded his system (finally) to the most recent enhancement pack and I thought to myself "What A Wonderful World" no no no, "could I write my example just with the standard tools now?". I've played around with my own NW 7.40 system and with just a little adjustment of the UI5 application it worked.
Just today Renald Wittwer commented on part I of the blog post that he used my example in one of his projects. Nice :-) But also this was the reason for this part II. If you are already on a current Netweaver release (I think with NW 7.31 this already should work), you can (or better should) use the SAP standard.
The Solution
Instead of using ADL by DJ Adams we use the really nice REST framework around the classes cl_rest_http_handler and cl_rest_resource.
Instead of using my JSON document class we use the standard CALL TRANSFORMATION
The Data
No changes to the original example.
The JSON document
DATA salesorders TYPESTANDARDTABLEOF ysithhsalesorder.
SELECT * FROM ysithhsalesorder
INTOTABLE @salesorders.
DATA(lo_json_writer) = cl_sxml_string_writer=>create(type = if_sxml=>co_xt_json ).
CALL TRANSFORMATION id SOURCE itab = salesorders RESULT XML lo_json_writer.
cl_demo_output=>display_json( lo_json_writer->get_output()).
Result
As you can see, the only difference is that the JSON fieldnames (labels) are all in Upper Case now.
The Call
The standard REST framework is quite similar build like DJ Adams ADL (dispatcher/handler -> resource) and self explaining.
The Handler
CLASS ysithh_rest_test DEFINITION
PUBLIC
FINAL
INHERITING FROM cl_rest_http_handler
CREATEPUBLIC.
PUBLICSECTION.
METHODS if_rest_application~get_root_handler REDEFINITION.
PROTECTEDSECTION.
PRIVATESECTION.
ENDCLASS.
CLASS ysithh_rest_test IMPLEMENTATION.
METHOD if_rest_application~get_root_handler.
DATA(lo_router) = NEW cl_rest_router().
lo_router->attach( iv_template = '/orders' iv_handler_class = 'YSITHH_REST_SALESORDERS_TEST').
ro_root_handler = lo_router.
ENDMETHOD.
ENDCLASS.
And again: don't forget to enter the dispatcher class into the ICF path via transaction SICF
The Resource
Not needed but interesting: In my resource, this time I'm also handling the so called "content negotiation", means that I'm asking what type of content the client wants to get as response.
CLASS ysithh_rest_salesorders_test DEFINITION
PUBLIC
INHERITING FROM cl_rest_resource
FINAL
CREATEPUBLIC.
PUBLICSECTION.
METHODS if_rest_resource~get REDEFINITION.
PROTECTEDSECTION.
PRIVATESECTION.
ENDCLASS.
CLASS ysithh_rest_salesorders_test IMPLEMENTATION.
METHOD if_rest_resource~get.
DATA(lo_entity) = mo_response->create_entity().
DATA:
lt_supp_cont_type TYPE string_table,
lv_content_type TYPE string,
lv_accept TYPE string.
* create the list or the content types supported by this REST server
lt_supp_cont_type = VALUE #(
( if_rest_media_type=>gc_appl_json )
( if_rest_media_type=>gc_appl_xml )
( if_rest_media_type=>gc_text_plain )
( if_rest_media_type=>gc_appl_atom_xml_feed )
).
* get accept value from REST client
lv_accept = mo_request->get_header_field( if_http_header_fields=>accept ).
* find the best "matching" content type from the supported list using the client's input
TRY.
lv_content_type = cl_rest_http_utils=>negotiate_content_type(
iv_header_accept = lv_accept
it_supported_content_type = lt_supp_cont_type ).
IF lv_content_type ISINITIAL. " no supported format found -> http 406
mo_response->set_status( cl_rest_status_code=>gc_client_error_not_acceptable ).
mo_response->set_reason( if_http_status=>reason_406 ).
RETURN.
ENDIF.
CATCH cx_rest_parser_error.
mo_response->set_status( cl_rest_status_code=>gc_server_error_internal ).
mo_response->set_reason( if_http_status=>reason_500 ).
RETURN.
ENDTRY.
mo_response->set_header_field(
EXPORTING
iv_name = 'Content-Type' " Header Name
iv_value = lv_content_type " Header Value
).
DATA salesorder TYPESTANDARDTABLEOF ysithhsalesorder.
SELECT * FROM ysithhsalesorder
INTOTABLE @salesorder.
CASE lv_content_type.
WHEN if_rest_media_type=>gc_appl_json.
" Transform data to JSON
DATA(lo_json_writer) = cl_sxml_string_writer=>create(type = if_sxml=>co_xt_json ).
CALL TRANSFORMATION id SOURCE itab = salesorder RESULT XML lo_json_writer.
lo_entity->set_content_type( if_rest_media_type=>gc_appl_json ).
lo_entity->set_binary_data( lo_json_writer->get_output()).
WHEN if_rest_media_type=>gc_appl_xml.
" Transform data to XML
CALL TRANSFORMATION id SOURCE itab = salesorder RESULT XML DATA(lv_xml).
lo_entity->set_content_type( if_rest_media_type=>gc_appl_xml ).
lo_entity->set_binary_data( lv_xml ).
WHEN if_rest_media_type=>gc_appl_atom_xml_feed.
" Transform data to Atom
DATA: ls_feed TYPE if_atom_types=>feed_s,
ls_entry TYPE if_atom_types=>entry_s.
ls_feed-id-uri = 'http://www.sap.com'.
GETTIMESTAMPFIELD ls_feed-updated-datetime.
LOOPAT salesorder ASSIGNING FIELD-SYMBOL(<f>).
ls_entry-title-text = | { <f>-id }-{ <f>-company_short }|.
CONVERT DATE sy-datlo
INTOTIMESTAMP ls_entry-updated-datetime TIMEZONE'UTC'.
ls_entry-title-type = if_atom_types=>gc_content_text.
APPEND ls_entry TO ls_feed-entries.
ENDLOOP.
DATA(lo_provider) = NEW cl_atom_feed_prov().
lo_provider->set_feed( ls_feed ).
lo_provider->write_to( lo_entity ).
WHEN if_rest_media_type=>gc_text_plain.
lo_entity->set_string_data('Content type:'&& lv_content_type ).
ENDCASE.
mo_response->set_status( cl_rest_status_code=>gc_success_ok ).
mo_response->set_header_field(
EXPORTING
iv_name = 'Access-Control-Allow-Origin' " Name of the header field
iv_value = '*' " HTTP header field value
).
ENDMETHOD.
ENDCLASS.
The Test
In this case we have to use a REST client like the Chrome plug-in "Postman", because we have to send the "Accept" header field for the "content negotiation".
No surprises here, result is like in part I except of the upper case labels.
The App
The UI5 application is the same like in part I. The only adjustments you have to make is the new URL in the controller.js and the labels in main.view.xml. The labels must be all in upper case now.
The Result
Yea, no differences
Epilogue
The JSON document class is not dead yet. If you need special handlings of the JSON input/output like date format, lower case, table appends etc. (more features you can find in the wiki on Github) you still can and should use the class. Also the class is still in "standard maintenance"
Appendix
- ABAP REST Library (Documentation)
- ABAP and JSON (Horst Kellers blog post with many links)