Quantcast
Channel: SCN : Blog List - ABAP Connectivity
Viewing all articles
Browse latest Browse all 68

JSON Adapter for ABAP Function Modules

$
0
0

JSON Adapter for ABAP Function Modules

This blog explains how to use an ABAP class that allows calling function modules with a URL and a JSON representation of ABAP data.

 

The code to the class presented in this blog is already available at SAP Code Exchange here:
https://cw.sdn.sap.com/cw/groups/json-adapter-for-abap-function-modules.

 

 

Introduction

ABAP Function Modules are a great thing. They provide encapsulation of functionality in an easy to use package. They are the basis of ABAP ability to interoperate with the external world, thanks to, mostly, two built-in features: the RFC library (where the F comes from "Function") and the SOAP adaptor, that allows SOAP compliant Web Services to be built from function modules.

ABAP Function Modules are also great for one more thing: they provide a name based parameter interface. This is something not new to ABAP developers, but this allows an incredible flexible way of parameter passing, including references, complex nested structures and tables. Shortly, with ABAP Function Modules you have the full power of the business suite at your service.

But, the RFC, while great, is a binary only library that could be cumbersome to use, and it is not available for all platforms. The SOAP adapter, while good for SOA enabled sites, could be also more complex that necessary and may have an overhead that makes it a non-option for some use cases, like, for example, data ready for consumption from user interfaces.


Just imagine how great could be to call a function module with a URL:

http://my.abap.server:8000/fmcall/rfc_system_info?format=json

 

and get the results of the function module call in JSON format:

 

{

     CURRENT_RESOURCES: 4,

     MAXIMAL_RESOURCES: 5,

     RECOMMENDED_DELAY: 0,

     RFCSI_EXPORT: 
{

          RFCPROTO: "011",

          RFCCHARTYP: "4103",

          RFCINTTYP: "LIT",

          RFCFLOTYP: "IE3",

          RFCDEST: "seppuku_NXK_00",

          RFCHOST: "seppuku",

          RFCSYSID: "NXK",

          RFCDATABS: "NXK",

          RFCDBHOST: "seppuku",

          RFCDBSYS: "ADABAS D",

          RFCSAPRL: "731",

          RFCMACH: " 390",

          RFCOPSYS: "Linux",

          RFCTZONE: " 3600",

          RFCDAYST: "",

          RFCIPADDR: "172.16.122.128",

          RFCKERNRL: "721",

          RFCHOST2: "seppuku",

          RFCSI_RESV: "",

          RFCIPV6ADDR: "172.16.122.128"

     }

}


Furthermore, imagine that the function module has input parameters, and we can pass them as part of the query string:

http://my.abap.server:8000/fmcall/bapi_flight_getdetail?airlineid=LH&connectionid=2402&flightdate=20130128&format=json

 

And get the details of the flight:


{

     ADDITIONAL_INFO: 
{

          FLIGHTTIME: 65,

          DISTANCE: "555.0000",

          UNIT: "KM",

          UNIT_ISO: "KMT",

          PLANETYPE: "DC-10-10",

          FLIGHTTYPE: ""

     },

     AVAILIBILITY: 
{

          ECONOMAX: 380,

          ECONOFREE: 0,

          BUSINMAX: 41,

          BUSINFREE: 0,

          FIRSTMAX: 18,

          FIRSTFREE: 0

     },

     FLIGHT_DATA: 
{

          AIRLINEID: "LH",

          AIRLINE: "Lufthansa",

          CONNECTID: "2402",

          FLIGHTDATE: "2013-01-28",

          AIRPORTFR: "FRA",

          CITYFROM: "FRANKFURT",

          AIRPORTTO: "SXF",

          CITYTO: "BERLIN",

          DEPTIME: "10:30:00",

          ARRTIME: "11:35:00",

          ARRDATE: "2013-01-28",

          PRICE: "242.0000",

          CURR: "EUR",

          CURR_ISO: "EUR"

     },

     EXTENSION_IN: [ ],

     EXTENSION_OUT: [ ],

     RETURN: 
[

          {

               TYPE: "S",

               ID: "BC_IBF",

               NUMBER: "000",

               MESSAGE: "Method was executed successfully",

               LOG_NO: "",

               LOG_MSG_NO: "000000",

               MESSAGE_V1: "",

               MESSAGE_V2: "",

               MESSAGE_V3: "",

               MESSAGE_V4: "",

               PARAMETER: "",

               ROW: 0,

               FIELD: "",

               SYSTEM: "NXK001"

          }

     ]

}

 

Isn't it great?

And then, imagine that you have to pass some ABAP structure or table to the function module. You could just go and write it in JSON and pass it to the function module in the payload of a POST request:

image001.png

 

This will be the response, showing that a new booking has been created:

 

image003.png


Have you noticed that in this case the name of the ABAP variables are in lower case? Have you also noticed the sequence "/lc" added to the end of the URL in figure 1?

ABAP internal variable names are always maintained in upper case. That's why this module defaults to upper case for the JSON string. But this is sometimes not pretty, and you may want to have the results to have var names in lower case. For this, just add a "lc" to the path info after the function module name.

 

 

Create the ICF Service

All this has been implemented in the ZCL_JSON_HANDLER class that is available in the JSON Adaptor for ABAP Funcion Modules code exchange project for you to download and use it.

Once you have the class installed, you only have to create an ICF handler in transaction SICF:

 

image005.png

Adjust logon data at will and then point your browser to the service URL. You are now ready to start!!

 

 

Output formats

JSON (the JavaScript Object Notation) is the basis for this class. The default output format is JSON and the only supported input format (apart form the query string params) is JSON. But, other possible output formats have been implemented.

JSON has been seeing an enormous increasing in its use in the last years, especially for its high suitability at being used directly in JavaScript based user interface communication, like HTML5 and AJAX. JSON can be consumed directly in a JavaScript program just by "evaling" it, and its low overhead has also find its way into many other languages and communication requirements.

JSON is normally invoked with the JavaScript XMLHttpRequest call, normally as an asynchronous call. But sometimes, the HTML tag <script> is used to place a JSON call. In this case, the <script> tag would need a real JavaScript program instead of a JSON string. For this, many servers implement a JSON "padded" call (or JSONP), in which the JSON data is sent as the parameter of a JavaScript function. This is normally indicated by the existence of a URL parameter with the name "callback", as seen here:


http://my.abap.server:8000/fmcall/rfc_system_info/lc?callback=jsfunction

 

Wich gives the following output:

 

jsfunction({"current_resources":4,"maximal_resources":5,"recommended_delay":0,"rfcsi_export":{"rfcproto":"011","rfcchartyp":"4103","rfcinttyp":"LIT","rfcflotyp":"IE3","rfcdest":"seppuku_NXK_00","rfchost":"seppuku","rfcsysid":"NXK","rfcdatabs":"NXK","rfcdbhost":"seppuku","rfcdbsys":"ADABAS D","rfcsaprl":"731","rfcmach":"  390","rfcopsys":"Linux","rfctzone":"  3600","rfcdayst":"","rfcipaddr":"172.16.122.128","rfckernrl":"721","rfchost2":"seppuku","rfcsi_resv":"","rfcipv6addr":"172.16.122.128"}});

 

This allows full operation for function modules calls from every kind of JavaScript application.

 

Once the basic JSON ABAP serialization was completed, I thought it would be a good idea to give you some choice in the output format, just as Google does with most of their public APIs.

 

Try this:

http://my.abap.server:8000/fmcall/rfc_system_info?format=xml

And you will get the output of the function call in a simple XML representation:


<RFC_SYSTEM_INFO>

  <CURRENT_RESOURCES>4</CURRENT_RESOURCES>

  <MAXIMAL_RESOURCES>5</MAXIMAL_RESOURCES>

  <RECOMMENDED_DELAY>0</RECOMMENDED_DELAY>

  <RFCSI_EXPORT>

    <RFCPROTO>011</RFCPROTO>

    <RFCCHARTYP>4103</RFCCHARTYP>

    <RFCINTTYP>LIT</RFCINTTYP>

    <RFCFLOTYP>IE3</RFCFLOTYP>   

    <RFCDEST>seppuku_NXK_00</RFCDEST>

    <RFCHOST>seppuku</RFCHOST>

    <RFCSYSID>NXK</RFCSYSID>

    <RFCDATABS>NXK</RFCDATABS>

    <RFCDBHOST>seppuku</RFCDBHOST>

    <RFCDBSYS>ADABAS D</RFCDBSYS>

    <RFCSAPRL>731</RFCSAPRL>

    <RFCMACH>390</RFCMACH>

    <RFCOPSYS>Linux</RFCOPSYS>

    <RFCTZONE>3600</RFCTZONE>

    <RFCDAYST/>

    <RFCIPADDR>172.16.122.128</RFCIPADDR>

    <RFCKERNRL>721</RFCKERNRL>

    <RFCHOST2>seppuku</RFCHOST2>

    <RFCSI_RESV/>

    <RFCIPV6ADDR>172.16.122.128</RFCIPV6ADDR>

  </RFCSI_EXPORT>

</RFC_SYSTEM_INFO>


And  this:

http://my.abap.server:8000/fmcall/rfc_system_info?format=yaml

In order to have the function module output in YAML format:

 

--- #YAML:1.0

CURRENT_RESOURCES: 4

MAXIMAL_RESOURCES: 5

RECOMMENDED_DELAY: 0

RFCSI_EXPORT:

  RFCPROTO: "011"

  RFCCHARTYP: "4103"

  RFCINTTYP: "LIT"

  RFCFLOTYP: "IE3"

  RFCDEST: "seppuku_NXK_00"

  RFCHOST: "seppuku"

  RFCSYSID: "NXK"

  RFCDATABS: "NXK"

  RFCDBHOST: "seppuku"

  RFCDBSYS: "ADABAS D"

  RFCSAPRL: "731"

  RFCMACH: "  390"

  RFCOPSYS: "Linux"

  RFCTZONE: "  3600"

  RFCDAYST: ""

  RFCIPADDR: "172.16.122.128"

  RFCKERNRL: "721"

  RFCHOST2: "seppuku"

  RFCSI_RESV: ""

  RFCIPV6ADDR: "172.16.122.128"


And  while I was at it, and remembering my Perl background, I thought it would be really fun to have ABAP just dump its data in Perl Data::Dumper format, which is quite close to JSON:

http://my.abap.server:8000/fmcall/rfc_system_info?format=perl

 

Have fun with Perl:

 

$RFC_SYSTEM_INFO = {'CURRENT_RESOURCES' => 4,'MAXIMAL_RESOURCES' => 5,'RECOMMENDED_DELAY' => 0,'RFCSI_EXPORT' => {'RFCPROTO' => '011','RFCCHARTYP' => '4103','RFCINTTYP' => 'LIT','RFCFLOTYP' => 'IE3','RFCDEST' => 'seppuku_NXK_00','RFCHOST' => 'seppuku','RFCSYSID' => 'NXK','RFCDATABS' => 'NXK','RFCDBHOST' => 'seppuku','RFCDBSYS' => 'ADABAS D','RFCSAPRL' => '731','RFCMACH' => '  390','RFCOPSYS' => 'Linux','RFCTZONE' => '  3600','RFCDAYST' => '','RFCIPADDR' => '172.16.122.128','RFCKERNRL' => '721','RFCHOST2' => 'seppuku','RFCSI_RESV' => '','RFCIPV6ADDR' => '172.16.122.128'}};

 

It shouldn't be difficult to create your favourite data representation, look at the code and develop your own ideas.

 

 

Available options

You can add several options as part of the URL path info and also as query string. This is a summary of them:

Path info options:

/lc -> outputs var names in lower case.

/yaml -> returns output in YAML format

/xml -> returns output in XML format

/perl -> returns output in Perl Data::Dumper format

(Path info options are not recommended if you want to keep a RESTful definition of a service).

Query string options:

?lowercase=X -> outputs var names in lower case (no available when using the built-in transformation ID)

?show_import_params=X -> include import params in the response

?callback=<callback_name> -> returns json output in enclosed in a JavaScript function call with name <callback_name>

?format=<data_format> -> returns output in specified <data_format>, which can be: json, xml, yaml, perl. Defaults to json.

Format is determined with the info sent by the client in the Accept HTTP header. If specified as an option, the info in the Accept header will not be used.

In the future, it is planned to have another option to create an input template for creating an input JSON message to the function module.

 

 

Acknowledgements and a bit of history

This little project doesn't come out of nothing, and as everything that is done nowadays in technology comes from the fact that we can see further as we are standing on the shoulders of giants. There are many JSON for ABAP development projects out there, and JSON support is already built-in in the core of ABAP. This nice feature is already available in this module for the JSON and XML converters.

 

It is a great characteristic of function modules implementation in ABAP that they have a dynamic way of defining function parameters. Additionally, function modules are already so well suited to be used in the model of direct request->response that is so in vogue today with the Web. I got the idea for all this from a very old blog post from Piers Harding: You don't need to use soap to keep your rpc clean. This is a very great article from 2004, and actually we implemented Piers' idea around that time, using YAML as serialization format. In 2006 we changed the main focus to JSON instead of YAML and we've been using that old version for a long time. Last year, some developments happening around made me have a look at all this, and could find the time to completely rewrite it cleanly and make it ready for publishing. So sorry for the delay, but it is better late than never. Hope this can still be useful to someone.

 

I could have never done this without the help of Juan Diez, who opened my eyes to the incredible richness of ABAP dynamic data programming. He actually developed parts of this and is the main user of this code. Thanks a lot Juan.


I also want to thank all my colleagues at SAP that have patiently listen to me (or at least pretended ) when I was talking about this and when I showed them the examples. I also thank them for their patience at standing at my ever changing mood: Ernesto, Félix, Miguel Angel, José, Cony, Cristina and Aviad, thanks a lot for being there.

 

 

Getting the code

The code is ready to be downloaded and used under the terms of use of SAP Code Exchange. You can get it here: https://cw.sdn.sap.com/cw/groups/json-adapter-for-abap-function-modules.

Join the project and please report bugs and ideas for improvement. Thank you!.

 


Viewing all articles
Browse latest Browse all 68

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>