Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

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
TypeDescription
DATA_REFInstance AttributePublicType ref to objectAttribute 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="https://blogs.sap.com/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>
10 Comments
Labels in this area