Skip to Content
Technical Articles

ABAP to JSON with Custom Transformation

Introduction:

ABAP to JSON conversion is very common requirement now-a-days. This can be done easily using the standard class “/UI2/CL_JSON”. This is the best way to convert ABAP to JSON. There are already couples of blog post already there. Below are the few links:-

https://wiki.scn.sap.com/wiki/display/Snippets/One+more+ABAP+to+JSON+Serializer+and+Deserializer

https://blogs.sap.com/2019/10/16/working-with-javascript-object-notation-json-format-in-abap-serialization-deserialization/

Problem Statement:

As mentioned already, “/UI2/CL_JSON” class is really handy and probably the best way to deal with “ABAP to JSON” conversion. But, recently I came across a requirement to convert ABAP to JSON  format but the class “/UI2/CL_JSON” was not present in the system due its current patch level(below screenshot for reference). So, I came up with the idea of utilization of “Custom Transformation” and “custom class” for ABAP to JSON conversion and thought to share it so that it will be helpful for some one.

So in this blog post we will discuss regarding the ABAP to JSON conversion using custom class and if you have “/UI2/CL_JSON” in the system, you can leave this blog post here and follow the the standard class(links to blog post given above).

Note: Apart from “/UI2/CL_JSON”, we do have another standard class “CL_SXML_STRING_WRITER” and a standard transformation “ID” for ABAP to JSON conversion.We do have a very nice blog post regarding the same. Below is the link.

https://blogs.sap.com/2013/01/07/abap-and-json/

But, the “PRETTY_MODE” option is not available in this, which is available in “/UI2/CL_ABAP” class in the method “SERIALIZE”. Hence, the case of the “attribute” name in the converted JSON will be always in “UPPER CASE”.To translate it into camel case or lower case, I came up with idea to create a custom class to serve the purpose.

Proposed Solution:

If you are still with me, I assume you have to come up with the custom class for ABAP to JSON Conversion. Here are the tasks we will follow in this blog post to create our own solution.

  • Creation of custom class “ZCL_JSON_UTILITY” which will be used to convert ABAP to JSON. This class will have below methods:
    • Convert ABAP structure to JSON format
    • Convert ABAP ITAB to JSON format
  • Creation of custom transformation for conversion of attribute case of JSON
  • A custom program using the class “ZCL_JSON_UTILITY” to show the ABAP to JSON conversion

So, lets start!

Step 1: Creation of class “ZCL_JSON_UTILITY” 

Let’s create the class “ZCL_JSON_UTILITY” in class builder(t-code – SE24).

Below constants are defined so that the attribute of the JSON can be formatted with correct case(as we have “PRETTY_NAME” parameter in the “SERIALIZE” method of class “/UI2/CL_JSON”.

We will have 3 method in this class. Each one we will discussed in details.

  • CONVERT_STUCT_TO_JSON
  • CONVERT_ITAB_TO_JSON
  • CONVERT_FNAME_CASE

Step 2: Creation of method – CONVERT_STUCT_TO_JSON

Let’s create a method “CONVERT_STUCT_TO_JSON”. Below is the screenshot of method signature.

In this method, we will convert the structure to JSON data. So, in this method we will do the following things

  • Call the standard transformation “ID” to convert structure to JSON format. The data will be in XSTRING format. If we convert it to string, we can see the JSON data. But, the attribute will be in “UPPER CASE”.
  • Pass the converted JSON data to the custom transformation “ZJSON_XML_TO_DIFF_CASEto convert it into the required case. We will discuss regarding this transformation soon.
  • Once the data is transformed as per the previous point, we will receive the converted JSON in xstring format. To convert it into string format, “CONVERT_FROM” method is used of the class “CL_ABAP_CODEPAGE”. There you go. You are done with the conversion. As simple as that.

Source code of the method:

DATA:
    lo_string_writer  TYPE REF TO cl_sxml_string_writer.

  DATA:
    lv_data       TYPE xstring,
    lv_arr_textin TYPE string,
    lv_arr_textot TYPE string,
    lv_len        TYPE i.

  TRY.

      "Create string writer for JSON
      lo_string_writer = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).

      "Call transformation 
      CALL TRANSFORMATION id SOURCE structure = im_s_data RESULT XML lo_string_writer.

      lv_data = lo_string_writer->get_output( ).

      FREE lo_string_writer.

      "Create string writer for JSON
      lo_string_writer = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).

      "Call transformation
      CALL TRANSFORMATION zjson_xml_to_diff_case
               PARAMETERS mode = im_v_case
                   SOURCE XML lv_data
                   RESULT XML lo_string_writer.

      "Get JSON in string format
      rt_v_json = cl_abap_codepage=>convert_from( lo_string_writer->get_output( ) ).

      lv_arr_textin = 'structure'.

      zcl_json_utility=>convert_fname_case(
        EXPORTING
          im_v_fname = lv_arr_textin
          im_v_case  = |{ im_v_case }|
        RECEIVING
          rt_v_fname = lv_arr_textot
      ).

      IF rt_v_json CS lv_arr_textot.

        lv_arr_textot = `{"` && lv_arr_textot && '":'.

        REPLACE FIRST OCCURRENCE OF lv_arr_textot IN rt_v_json WITH ''.

        lv_len = strlen( rt_v_json ) - 1.

        IF lv_len GT 0.
          rt_v_json = rt_v_json+0(lv_len).
        ENDIF.

      ENDIF.
    CATCH cx_root.
      RAISE EXCEPTION TYPE zcx_json_conversion_error.
  ENDTRY.

Step 3: Creation of method – CONVERT_ITAB_TO_JSON

In this method, we have to convert ITAB data to JSON format. The same task are performed in this.

Below is the screenshot of the signature.

The task we followed in the previous step, similarly we have to do the thing in this method too. Below is the complete source code.

Source code of the method:

 DATA:
    lo_string_writer  TYPE REF TO cl_sxml_string_writer.

  DATA:
    lv_data       TYPE xstring,
    lv_arr_textin TYPE string,
    lv_arr_textot TYPE string,
    lv_len        TYPE i.

  TRY.

      "Create string writer for JSON
      lo_string_writer = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).

      "Call transformation
      CALL TRANSFORMATION id SOURCE array = im_t_table RESULT XML lo_string_writer.

      lv_data = lo_string_writer->get_output( ).

      FREE lo_string_writer.

      "Create string writer for JSON
      lo_string_writer = cl_sxml_string_writer=>create( type = if_sxml=>co_xt_json ).

      "Call transformation
      CALL TRANSFORMATION zjson_xml_to_diff_case
               PARAMETERS mode = im_v_case
                   SOURCE XML lv_data
                   RESULT XML lo_string_writer.

      "Get JSON
      rt_v_json = cl_abap_codepage=>convert_from( lo_string_writer->get_output( ) ).

      lv_arr_textin = 'Array'.

      zcl_json_utility=>convert_fname_case(
        EXPORTING
          im_v_fname = lv_arr_textin
          im_v_case  = |{ im_v_case }|
        RECEIVING
          rt_v_fname = lv_arr_textot
      ).

      IF rt_v_json CS lv_arr_textot.

        lv_arr_textot = `{"` && lv_arr_textot && '":'.

        REPLACE FIRST OCCURRENCE OF lv_arr_textot IN rt_v_json WITH ''.

        lv_len = strlen( rt_v_json ) - 1.

        IF lv_len GT 0.
          rt_v_json = rt_v_json+0(lv_len).
        ENDIF.

      ENDIF.

    CATCH cx_root.
      RAISE EXCEPTION TYPE zcx_json_conversion_error.
  ENDTRY.

Step 4: Creation of method – CONVERT_FNAME_CASE

This method will convert the case of the attribute to different cases.This method is called in the custom transformation “ZJSON_XML_TO_DIFF_CASE” to translate the case of the attribute to required case.

The logic of this method is very simple. Hence, not covering the same in details.

Below is the complete source code

Source code of the method:

  DATA:
    lv_str TYPE string.

  CLEAR rt_v_fname.

  CHECK im_v_fname IS NOT INITIAL.

  CASE im_v_case.

    WHEN co_ftype_lowercase.
      rt_v_fname = to_lower( im_v_fname ).

    WHEN co_ftype_uppercase.
      rt_v_fname = to_upper( im_v_fname ).

    WHEN co_ftype_camelcase_upper.

      lv_str     = to_mixed( im_v_fname ).
      rt_v_fname = to_upper( im_v_fname(1) ) && lv_str+1.

    WHEN co_ftype_camelcase.
      lv_str     = to_mixed( im_v_fname ).
      rt_v_fname = to_lower( im_v_fname(1) ) && lv_str+1.

  ENDCASE.

  REPLACE all OCCURRENCES OF '"' in rt_v_fname WITH ''.

Step 5: Creation of Custom Transformation “ZJSON_XML_TO_DIFF_CASE”

We have to create a custom transformation with the provided name in t-code “XSLT_TOOL”. The transformation type will “X – XSLT Program”.

Below is the code written in XSLT transformation. You can see the method “CONVERT_FNAME_CASE” is called to transform the case of the attribute name to different case.

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sap="http://www.sap.com/sapxsl" xmlns:f="FCT" exclude-result-prefixes="f" version="1.0">

  <sap:external-function class="ZCL_JSON_UTILITY" kind="class" method="CONVERT_FNAME_CASE" name="f:toCC">
    <sap:argument param="IM_V_FNAME"   type="string"/>
    <sap:argument param="IM_V_CASE"    type="string"/>
    <sap:result   param="RT_V_FNAME"   type="string"/>
  </sap:external-function>

  <xsl:param name="MODE"/>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="@*">
    <xsl:attribute name="name">
      <xsl:value-of select="f: toCC( string( . ), string( $MODE ) )"/>
    </xsl:attribute>
  </xsl:template>
</xsl:transform>

Step 6: Creation of exception class “ZCX_JSON_CONVERSION_ERROR” used in the method described in Step 2 and 3.

Below is the screenshot of exception class created for JSON conversion error.

Step 7: Creation of program “ZABAP_2_JSON” ABAP to JSON using the custom class created above.

Below is the source code of the program to convert ABAP to JSON using custom class. Different values been passed to the parameter “IM_V_CASE” of the method and the attribute name is translated accordingly in JSON.

REPORT zabap_2_json.

DATA:
  BEGIN OF ls_data,
    material_no   TYPE mara-matnr,
    material_desc TYPE string,
  END OF ls_data,

  lt_data LIKE TABLE OF ls_data INITIAL SIZE 0.

DATA:
 lv_text TYPE string.

TRY.

    ls_data-material_no   = '900'.
    ls_data-material_desc = 'Material 1'.

    cl_demo_output=>begin_section( `Structure to JSON(Camel case):`).

    zcl_json_utility=>convert_stuct_to_json(
      EXPORTING
        im_s_data = ls_data
        im_v_case = zcl_json_utility=>co_ftype_camelcase
      RECEIVING
        rt_v_json = lv_text
    ).
    cl_demo_output=>write( data = lv_text ).
    cl_demo_output=>end_section( ).

    cl_demo_output=>begin_section( `Structure to JSON(Camel case upper):` ).

    zcl_json_utility=>convert_stuct_to_json(
      EXPORTING
        im_s_data = ls_data
        im_v_case = zcl_json_utility=>co_ftype_camelcase_upper
      RECEIVING
        rt_v_json = lv_text
    ).
    cl_demo_output=>write( data = lv_text ).
    cl_demo_output=>end_section( ).

    cl_demo_output=>begin_section( `Structure to JSON(lower case):` ).

    zcl_json_utility=>convert_stuct_to_json(
      EXPORTING
        im_s_data = ls_data
        im_v_case = zcl_json_utility=>co_ftype_lowercase
      RECEIVING
        rt_v_json = lv_text
    ).
    cl_demo_output=>write( data = lv_text ).
    cl_demo_output=>end_section( ).

    cl_demo_output=>begin_section( `Structure to JSON(upper case):` ).

    zcl_json_utility=>convert_stuct_to_json(
      EXPORTING
        im_s_data = ls_data
        im_v_case = zcl_json_utility=>co_ftype_uppercase
      RECEIVING
        rt_v_json = lv_text
    ).
    cl_demo_output=>write( data = lv_text ).
    cl_demo_output=>end_section( ).

    ls_data-material_no   = '900'.
    ls_data-material_desc = 'Material 1'.

    APPEND ls_data TO lt_data.

    ls_data-material_no   = '901'.
    ls_data-material_desc = 'Material 2'.

    APPEND ls_data TO lt_data.

    cl_demo_output=>begin_section( `ITAB to JSON(camel case):` ).

    zcl_json_utility=>convert_itab_to_json(
      EXPORTING
        im_t_table = lt_data
        im_v_case = zcl_json_utility=>co_ftype_camelcase
      RECEIVING
        rt_v_json = lv_text
    ).

    cl_demo_output=>write( data = lv_text ).
    cl_demo_output=>end_section( ).


    cl_demo_output=>display( ).

  CATCH zcx_json_conversion_error.    " Error during JSON Conversion
ENDTRY.

Output of the program:

Do you need a case which is not in this program? Go to the step 4 and change the logic and populate “RT_V_FNAME” as per the format you need. Your purpose will be solved.

Conclusion:

So, in this blog post, we have converted the ABAP to JSON data using the custom class. Hope this will helpful for someone if “/UI2/CL_ABAP” class is not available in the system.Cheers!

2 Comments
You must be Logged on to comment or reply to a post.
  • I have to admit that unnecessary dashes in “now-a-days” initially triggered my OCD 🙂 but am willing to overlook that and even the use of Hungarian notation in the code because otherwise it’s actually a really good blog.

    It starts very nicely with an explanation of the use case and links to other blogs. This sets the scene perfectly for the readers. It offers the task overview and then explains the specific steps. The code is well-formatted, text is neat, clear, and the thought process is easy to follow.

    Others might have some input regarding the ABAP code (especially with regards to Clean Code) but, as far as the blog itself goes, this is an example of what a good technical article should look like.

    Well done and keep writing!

    • Thanks Jelena for the suggestions. Point noted and will keep these points in mind for future blog posts.

      I am glad you liked the blog post.

      Thanks,

      Gourab