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: 
cesar_at_sap
Advisor
Advisor

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 this project is available in this GitHub repository: cesar-sap/abap_fm_json · GitHub.

If you have problems installing it via SAPLink or abapGit, try the ABAP transport files here: abap_fm_json/transport/ at master · cesar-sap/abap_fm_json · GitHub.


Introduction

ABAP Function Modules is one of the best and most useful features of the ABAP application server. 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=20...

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:


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

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?callback=jsfunction&lowercase=X

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  query string. This is a summary of them:

  • ?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 enclosed in a JavaScript function call with name <callback_name> (also known as jsonp, for json padded)
  • ?format=<data_format> -> returns output in specified <data_format>, which can be: json, xml, yaml, perl. Defaults to json.


Output format is determined with the info sent by the client in the Accept HTTP header.

If specified as a query string option, it will take precedence over the Accept header.

Installation

The code is provided to you in saplink NUGG format. The code is contained in an ABAP class called ZCL_JSON_HANDLER. You just have to put it in an ICF handler. Some considerations for installation follow:


Security and ABAP roles


The ZCL_JSON_HANDLER class contains a user defined ABAP authority check object called Z_JSON. This object contains a single field called FMNAME. In order to protect access to function modules it is highly recommended that each user has a list of the function modules he or she is authorized to call with this handler. A single * will effectively allow access to all function modules.

Due to limitations in saplink, the authorization object is not created when you import the nugget. You have to manually create it with transaction SU21.

Create the ICF Service

Once you have the class installed and have created the authorization object, the next step is to create an ICF handler in transaction SICF:

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

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 horst.keller/blog/2013/01/07/abap-and-json. This nice feature is already available in this module for the JSON and XML converters.

A great characteristic of function modules in ABAP is that they have a dynamic way of defining function parameters. Additionally, function modules are already very well suited to be used in the model of direct request->response that is so in vogue today with the Web. So we only needed a way of escaping the RFC protocol and put all this richness directly accessible to HTTP calls. 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 article is from year 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 in internal and partner projects. 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 Diaz, 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 :smile: ) 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 used to be available in Code Exchange here: https://code.sdn.sap.com/spaces/json-adapter-for-abap-function-modules/milestones. The link is not available anymore, as SAP decided that Code Exchange should die.


The code is now in GitHub here: cesar-sap/abap_fm_json · GitHub.


if you have problems getting the code please send me a private message.


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

180 Comments