Skip to Content

As web, mobile applications and cloud computing has become main trends, SOA (service oriented architecture) become core at applications. Morever, business demands more responsive web and mobile applications. The standard post-back based web applications has already been outdated. AJAX (Asynchronous JavaScript and XML) become inevitable. Script libraries like JQuery made web pages look like desktop applications and has risen the expectations of business from IT. So much things has been changed but client-server architecture has not been changed. We still write ABAP code in BSP web applications. Whatever we do in user interface in our web applications we still have to communicate with backend systems. AJAX gives us a big advantage at that point. We can consume web services and call this services from client-side scripts in our BSP Applications. We can write services that returns JSON objects and this JSON objects can be used in client-side javascripts easily.

In this article I will introduce the JSON serializer class that I have written. JSON Serializer class simply serializes any ABAP object with all attributes to JSON object.

What is JSON?

JSON is syntax for storing and exchanging text information. Much like XML. JSON is smaller than XML, and faster and easier to parse (http://www.w3schools.com/json/). Briefly we can say that JSON is string represantation of an object

As an example consider we have a class ZJSONINPUT. It has an attribute FLIGHT as a table:

data : lrf_zinput type ref to ZJSONINPUT,

       lrf_json_processor type ref to Z_CL_JSON_PROCESSOR.

CREATE object LRF_ZINPUT.

“Fill some data to input

data lt_flight type FLIGHTTAB.

SELECT * FROM SFLIGHT up to 2 rows INTO table lt_flight.

data lt_flight type FLIGHTTAB.

SELECT * FROM SFLIGHT up to 2 rows INTO table lt_flight.

lrf_zinput->flight[] = lt_flight[].

JSON equivalent of the class is:

{

“FLIGHT”:

[

{“MANDT”:”160″,”CARRID”:”AA”,”CONNID”:”0017″,”FLDATE”:”20041007″,”PRICE”:”1413.93 “,”CURRENCY”:”USD”,”PLANETYPE”:”747-400″,”SEATSMAX”:”660 “,”SEATSOCC”:”157-“,”PAYMENTSUM”:”0.00 “,”SEATSMAX_B”:”0 “,”SEATSOCC_B”:”0 “,”SEATSMAX_F”:”0 “,”SEATSOCC_F”:”0 “},

{“MANDT”:”160″,”CARRID”:”AA”,”CONNID”:”0017″,”FLDATE”:”20041008″,”PRICE”:”1413.93 “,”CURRENCY”:”USD”,”PLANETYPE”:”747-400″,”SEATSMAX”:”660 “,”SEATSOCC”:”115-“,”PAYMENTSUM”:”57433.79 “,”SEATSMAX_B”:”0 “,”SEATSOCC_B”:”0 “,”SEATSMAX_F”:”0 “,”SEATSOCC_F”:”0 “}

]

}

As you see it’s very simple. General syntax is  “key”:”value”. Anyway you don’t need to learn details of json with my json serializer class.

JSON SERIALIZER CLASS

To convert ABAP Class to JSON I ve created class Z_CL_JSON_PROCESSOR. Z_CL_JSON_PROCESSOR loads the abap object in its class constructor. In it’s SERIALIZE method, it serialize the load abap object instance that’s going to be serialized to  JSON string.

Attributes:

Attribute Level Visibility Type Description
DATA_REF Instance Attribute Public Type ref to object Attribute to store abap object instance

Methods:

  • Constructor:

method CONSTRUCTOR

importing

      DATA type ref to OBJECT .

  me->data_ref = data.

endmethod.


  • Serialize: Convert abap object to JSON string

method SERIALIZE

exporting

      E_JSON type STRING .

CALL  method RECURSE

   exporting

     DATA   = me->DATA_REF

   importing

     E_JSON = E_JSON

   .

concatenate ‘{‘ E_JSON ‘}’ into E_JSON.

endmethod.

  • Recurse: It’s called by the SERIALIZE method. Attributes of the classed is iterated and returned to sub JSON parts. Since the class that’s going to be serialized may contain other classes this method is called recursively.

method RECURSE

importing

      DATA type ref to OBJECT

    exporting

     E_JSON type STRING .

  “Get attributes of class

  DATA: l_typedesc TYPE REF TO cl_abap_typedescr.

  data: l_classdesc TYPE REF TO cl_abap_classdescr.

  data: l_attribute TYPE abap_attrdescr.

  data: l_structdesc type ref to CL_ABAP_STRUCTDESCR,

        l_tabledesct type ref to CL_ABAP_TABLEDESCR,

        l_table_line type ref To cl_abap_structdescr.

  data lv_datatype type abap_typekind.

  FIELD-SYMBOLS : <att> TYPE ANY ,

                 <componentvalue> type any,

                 <fs_table> type standard table,

                 <tableline> type any,

                 <fieldval> TYPE ANY..

  data lrf_descr  Type Ref To CL_ABAP_TYPEDESCR.

  data lv_temp_json type string.

  data : lt_component_table TYPE ABAP_COMPONENT_TAB,

         ls_component_table type  abap_componentdescr.

  field-symbols <key_comp_wa> TYPE abap_keydescr.

  data lv_itemval type string.

  CALL METHOD cl_abap_classdescr=>describe_by_object_ref

    EXPORTING

      p_object_ref = DATA

    RECEIVING

      p_descr_ref  = l_typedesc.

  l_classdesc ?= l_typedesc.

  LOOP AT L_CLASSDESC->ATTRIBUTES into l_attribute.

    CLEAR LV_TEMP_JSON.

    ASSIGN DATA->(L_ATTRIBUTE-NAME) TO <att>.

    IF <att> IS assigned.

      “Get type of attribute

      “lv_datatype = CL_ABAP_DATADESCR=>GET_DATA_TYPE_KIND( P_DATA =  <att> ).

      lrf_descr ?= CL_ABAP_TYPEDESCR=>DESCRIBE_BY_DATA( P_DATA = <att> ).

      IF LRF_DESCR IS NOT initial.

        “Loop attributes of class

        CASE LRF_DESCR->KIND.

          WHEN CL_ABAP_TYPEDESCR=>KIND_REF.”IF it is class run recurce again

            data lv_classjson type string.

            clear lv_classjson.

            CALL METHOD me->RECURSE

              EXPORTING

                DATA   = <att>

              IMPORTING

                E_JSON = LV_TEMP_JSON.

            concatenate ‘”‘ L_ATTRIBUTE-name ‘”:{‘  LV_TEMP_JSON ‘}’ into lv_classjson.

            IF E_JSON IS initial.

              concatenate e_json LV_CLASSJSON  into e_json.

            ELSE.

              concatenate e_json ‘,’ LV_CLASSJSON  into e_json.

            ENDIF.

          WHEN CL_ABAP_TYPEDESCR=>KIND_ELEM. “if it is elementary type add it to json

            move <att> to lv_itemval.

            CALL METHOD me->ESCAPE_CHARS

              CHANGING

                C_TEXT = LV_ITEMVAL.

            data lv_elemjson type string.

            clear lv_elemjson.

            concatenate ‘”‘ L_ATTRIBUTE-name ‘”:”‘ lv_itemval ‘”‘ into LV_ELEMJSON.

            IF E_JSON IS initial.

              concatenate e_json lv_elemjson  into e_json.

            ELSE.

              concatenate e_json ‘,’ LV_ELEMJSON  into e_json.

            ENDIF.

          WHEN CL_ABAP_TYPEDESCR=>KIND_STRUCT.”if it’s structure loop throught the components of structure

            data: lv_structjson  type string,

            lv_compjson type string.

            clear : lv_structjson,lv_compjson.

            l_typedesc = CL_ABAP_STRUCTDESCR=>DESCRIBE_BY_DATA( P_DATA = <att> ).

            l_structdesc ?= l_typedesc.

            LT_COMPONENT_TABLE = L_STRUCTDESC->GET_COMPONENTS( ).

            concatenate ‘”‘ L_ATTRIBUTE-name ‘”:{‘ into LV_STRUCTJSON.

            LOOP AT LT_COMPONENT_TABLE into LS_COMPONENT_TABLE.

              ASSIGN component LS_COMPONENT_TABLE-NAME  of structure <att> to <COMPONENTVALUE>.

              move <COMPONENTVALUE> to  lv_itemval.

              CALL METHOD me->ESCAPE_CHARS

                CHANGING

                  C_TEXT = LV_ITEMVAL.

              IF lv_compjson IS initial.

                concatenate lv_compjson ‘”‘ LS_COMPONENT_TABLE-NAME  ‘”‘ ‘:’ ‘”‘ lv_itemval ‘”‘ into lv_compjson.

              ELSE.

                concatenate lv_compjson ‘,”‘ LS_COMPONENT_TABLE-NAME  ‘”‘ ‘:’ ‘”‘ lv_itemval ‘”‘ into lv_compjson.

              ENDIF.

            ENDLOOP.

            concatenate LV_STRUCTJSON lv_compjson into LV_STRUCTJSON.

            concatenate LV_STRUCTJSON ‘}’ into LV_STRUCTJSON.

            IF e_json is initial.

              concatenate e_json LV_STRUCTJSON into e_json.

            ELSE.

              concatenate e_json ‘,’ LV_STRUCTJSON into e_json.

            ENDIF.

          WHEN CL_ABAP_TYPEDESCR=>KIND_TABLE.

            data : lv_tablejson type string,

                   LV_COLUMNJSON type string,

                   lv_linejson type string.

            CLEAR : LV_TABLEJSON,LV_COLUMNJSON,LV_LINEJSON.

            l_typedesc = CL_ABAP_TABLEDESCR=>DESCRIBE_BY_DATA( P_DATA = <att>  ).

            L_TABLEDESCT ?= L_TYPEDESC.

            DATA lv_without_column type boolean_flg.

            clear lv_without_column.

            TRY .

                l_table_line ?= L_TABLEDESCT->GET_TABLE_LINE_TYPE( ).

              CATCH CX_SY_MOVE_CAST_ERROR.

                lv_without_column = ‘X’.

            ENDTRY.

              concatenate ‘”‘ L_ATTRIBUTE-name ‘”:[‘ into lv_tablejson.

            ASSIGN  <att> to <fs_table>.

            data lwa_comp TYPE abap_compdescr.

            LOOP AT <fs_table> assigning <TABLELINE>.

              CLEAR LV_COLUMNJSON.

              IF lv_without_column IS INITIAL.

                LOOP AT L_TABLE_LINE->COMPONENTS into lwa_comp.

                  ASSIGN COMPONENT lwa_comp-name OF STRUCTURE <TABLELINE> TO <fieldval>.

                  move <FIELDVAL> to LV_ITEMVAL.

                  CALL METHOD me->ESCAPE_CHARS

                    CHANGING

                      C_TEXT = LV_ITEMVAL.

                  IF LV_COLUMNJSON is initial.

                    concatenate LV_COLUMNJSON ‘”‘ lwa_comp-name  ‘”:”‘ LV_ITEMVAL ‘”‘ into LV_COLUMNJSON.

                  ELSE.

                    concatenate LV_COLUMNJSON ‘,”‘ lwa_comp-name  ‘”:”‘ LV_ITEMVAL ‘”‘ into LV_COLUMNJSON.

                  ENDIF.

                endloop.

              ELSE. “Without column name

                MOVE <TABLELINE> TO lv_itemval.

                IF LV_COLUMNJSON is initial.

                  concatenate LV_COLUMNJSON ‘”VAL”:”‘ LV_ITEMVAL ‘”‘ into LV_COLUMNJSON.

                ELSE.

                  concatenate LV_COLUMNJSON ‘,”VAL”:”‘ LV_ITEMVAL ‘”‘ into LV_COLUMNJSON.

                ENDIF.

              ENDIF.

              IF LV_LINEJSON is initial.

                concatenate LV_LINEJSON ‘{‘ LV_COLUMNJSON ‘}’ into lv_linejson.

              ELSE.

                concatenate LV_LINEJSON ‘,{‘ LV_COLUMNJSON ‘}’ into lv_linejson.

              ENDIF.

            endloop.

            concatenate LV_TABLEJSON LV_LINEJSON  ‘]’ into LV_TABLEJSON .

            IF E_JSON IS initial.

              concatenate E_JSON LV_TABLEJSON into e_json.

            ELSE.

              concatenate E_JSON ‘,’ LV_TABLEJSON into e_json.

            ENDIF.

        ENDCASE.

      ENDIF.

    ENDIF.

  endloop.

endmethod.

  • Escape_chars: Replaces special characters in JSON text with escape characters

method ESCAPE_CHARS.

    changing

      !C_TEXT type STRING .

  REPLACE ALL occurrences of ‘”‘ IN C_TEXT with ‘#”#”‘.

  REPLACE ALL occurrences of ‘\’ IN C_TEXT with ‘#\#’.

  REPLACE ALL occurrences of ‘#”#”‘ IN C_TEXT with ‘\\”‘.

  REPLACE ALL occurrences of ‘#\#’ IN C_TEXT with ‘\\u005C’.

endmethod.

To see in action: First we must create a BSP page (page with flow logic). In BSP Page first we create the object that we want to serialize and fill with data:

<%

data : lrf_zinput type ref to ZJSONINPUT,

       lrf_json_processor type ref to Z_CL_JSON_PROCESSOR.

CREATE object LRF_ZINPUT.

data lt_flight type FLIGHTTAB.

SELECT * FROM SFLIGHT up to 2 rows INTO table lt_flight.

lrf_zinput->flight[] = lt_flight[].

lrf_zinput->caption = ‘ “Json Serialize \Demonstration ‘

%>

After that we create our serializer class instance and load our object to be serialized and call serializee method. SERIALIZE Method will return JSON text

<%

Create object LRF_JSON_PROCESSOR

  exporting

    DATA = lrf_zinput.

call method LRF_JSON_PROCESSOR->SERIALIZE

  importing

    E_JSON = lv_json.

%>

Now we have the JSON text. What we need to do is to pass the JSON text Javascript and use the object in Javasript. We need some Javascript coding. I used JQuery so we have to reference jquery in our BSP page. jQuery.parseJSON will return it to javascript object. After that we can access the atrributes in the abap class from javascript. For example, obj.CAPTION will return the CAPTION that we set in ABAP (serverside). Likely, obj.FLIGHT[0].CARRID will return the CARRID of the first row of FLIGHT table. As you see we can access the server side object from client side!

 <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" />
      <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
      <script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script>
<span id="caption></span>
<span id="CARRID1"></span>
<span id="CONNID1"></span>
<span id="CARRID2"></span>
<span id="CONNID2"></span>
<script>
var obj  = jQuery.parseJSON('<%= lv_json %>');
document.getElementById("caption").innerHTML = obj.CAPTION;
document.getElementById("CARRID1").innerHTML = obj.FLIGHT[0].CARRID;
document.getElementById("CONNID1").innerHTML = obj.FLIGHT[0].CONNID;
document.getElementById("CARRID2").innerHTML = obj.FLIGHT[1].CARRID;
document.getElementById("CONNID2").innerHTML = obj.FLIGHT[1].CONNID;
</script>

The complete code in BSP Page is:

<%@page language="abap"%>
<%@extension name="htmlb" prefix="htmlb"%>
<%
data : lrf_zinput type ref to ZJSONINPUT,
       lrf_json_processor type ref to Z_CL_JSON_PROCESSOR.
CREATE object LRF_ZINPUT.
  .
data lt_flight type FLIGHTTAB.
SELECT * FROM SFLIGHT up to 2 rows INTO table lt_flight.
lrf_zinput->flight[] = lt_flight[].
lrf_zinput->caption = ' "Json Serialize \Demonstration '.
data lv_json type string.
clear lv_json.
Create object LRF_JSON_PROCESSOR
  exporting
    DATA = lrf_zinput.
call method LRF_JSON_PROCESSOR->SERIALIZE
  importing
    E_JSON = lv_json
  .
%>
<htmlb:content design="design2003">
  <htmlb:page title = " ">
 <link rel="stylesheet" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" />
      <script src="http://code.jquery.com/jquery-1.9.1.js"></script>
      <script src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script>
      <link rel="stylesheet" href="/resources/demos/style.css" />
    <htmlb:form>
<span id="caption"></span>
 <table border="1">
   <tr>
<th>Airline Code</th>
<th>Flight Connection Number</th>
<th>Flight date</th>
<th>Airfare</th>
<th>Local currency of airline</th>
<th>Aircraft Type</th>
<th>Maximum capacity in economy class</th>
<th>Occupied seats in economy class</th>
<th>Total of current bookings</th>
<th>Maximum capacity in business class</th>
<th>Occupied seats in business class</th>
<th>Maximum capacity in first class</th>
<th>Occupied seats in first class</th>
   </tr>
  <script>
var obj  = jQuery.parseJSON('<%= lv_json %>');
document.getElementById("caption").innerHTML = obj.CAPTION;
var i;
for(i=0;i<obj.FLIGHT.length;i++)
{
  document.write("<tr>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].CARRID);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].CONNID);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].FLDATE);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].PRICE);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].CURRENCY);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].PLANETYPE);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].SEATSMAX);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].SEATSOCC);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].PAYMENTSUM);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].SEATSMAX_B);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].SEATSOCC_B);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].SEATSMAX_F);
  document.write("</td>");
  document.write("<td>");
  document.write(obj.FLIGHT[i].SEATSOCC_F);
  document.write("</td>");
document.write("</tr>");
}
</script>
</table>
    </htmlb:form>
  </htmlb:page>
</htmlb:content>
To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

    1. Umit Coskun Aydinoglu Post author

      Hello Uwe, CALL TRANSFORMATION works correct if class does not include any class. If it includes reference attribute it doesn’t create json correctly. This is the motivation of creating my JSON serializer.

      (0) 
  1. Mingzuo Shen

    Great!

    in my 7.31 system,

    I had to replace the DATA with data_ref in the RECURSE method.

    Otherwise I got Exception condition “REFERENCE_IS_INITIAL” raised.

    CALL METHOD cl_abap_classdescr=>describe_by_object_ref

         EXPORTING

           p_object_ref = data_ref

         RECEIVING

           p_descr_ref  = l_typedesc.


    tried zCL_TREX_JSON_SERIALIZER to serialize.

    tried CALL FUNCTION ‘Z_JSON_OUT’ (in ZJSON).

    both of the these gave me error:

    Conversion of type “r” to type “g” not supported.


    tried CALL TRANSFORMATION id SOURCE

    E_JSON = lrf_zinput RESULT XML writer.

    This does not cause an error, but the JSON string

    does is incorrect. the resulting JSON string contains:

    %ref, %heap, %type, %val.

    Only this author’s class worked serializing this particular ABAP object!

    (0) 
  2. Ashok Kumar M

    Hello Experts,

    I am making a https call from abap to an external system and getting the data in json format. can someone please tell me how to convert this json data into an internal table.

    Many thanks.

    Best Regards,

    Ashok.

    (0) 
  3. Mitul Adhia

    Hi Umit ,

    I have multiple internal tables which i want them to convert into single json output how do I use your api as it has single input parameter ?

    Best Regards,

    Mitul

    (0) 
    1. Umit Coskun Aydinoglu Post author

      Hello Mitul,

      My object takes abap object as parameter. You can create an abap class and define multiple internal tables as attribute of that class.

      In fact, this method is out dated. I suggest using OData services if you have netweaver gateway.

      Kind Regards,

      Umit Coşkun

      (0) 
      1. Mitul Adhia

        Hi Umit ,

        I tried that but internally throws exception during serialization .”

        You attempted to move one data object to another.

        This is not possible here because the conversion of a data object

        of type “REF TO Z_OUTPUT” to type “STRING” is not supported.

        ” … the code snippet is given below ..

        CREATE OBJECT lr_z_output .
        lr_z_output->set_data( it_data = carrier_tab ).
        CREATE OBJECT lr_json_serializer EXPORTING data = lr_z_output .
        lr_json_serializer->serialize( ).

        Is it fine for you to provide one sample example .?

        Best Regards,

        Mitul

        (0) 
      2. Swetha Shivakumar

        Hi Umit,

        Based on your response for the creating the ABAP to JSON serializer using the Odata services, I would like to know if you can suggest any options for resolving my issue.

        I have created a Odata GET service for consuming the request from Crawler using the POST service of REST and give a resultant details from service to UI5.

        I am trying to build the URL, converting from ABAP to JSON and post the service. URL is built successfully.  ABAP to JSON conversion happens using the class cl_trex_json_serializer =>get_data().

        At the time of

        lo_response = lo_rest_client->if_rest_client~get_response_entity( ). I get an internal error 500.

        Not able to resolve this.

        Could you please suggest any solution or use any alternate method?

        (0) 
  4. Umit Coskun Aydinoglu Post author

    Hello,

    SAP Standard /UI2/CL_JSON class can be used for json serialization. As I see the DUMP method of this class is very much like my code 🙂 with some enhancements. I think, I must be proud that my code has become SAP standard 🙂

    Kind Regards,

    Coşkun

    (0) 

Leave a Reply