Skip to Content

I received a couple of questions on how to handle a String in JSON Format in ABSL. You can, for example, call a REST service (see for example Integration of SAP OnDemand Products with Business Suite via REST/OData Services) and the result is a response in JSON. In contrast to a REST service, a SOAP service that always comes with a service description (WSDL) and from this, SAP Cloud Application Studio generate a data type and a transformation from the message into the data type (see External Web Service Integration in SAP Cloud Application Studio). For a REST service a formal service definition (WSDL) is typically not available. The response of the REST service is a string, for example in JSON format (or in XML or other format.

The following code snippet shows how you can parse a string in JSON format into a hierarchical table. Then, of course, you have to loop over the table and find the business content that you are interested in.

Please note, that I do not give the guarantee, that the code can handle all kind of JSON (for example, I did not consider escape characters). But if you find some cases that are not considered by my code, you can easily enhance and improve it and adopt it to you concrete web service.

The code makes use of the FindRegex() function. You can find the documentation on this service and more services for string handling in the studio documentation (http://help.sap.com/studio_cloud). You can find additional information on regular expression in the internet (for example search for “ABAP”+”find”+”regex”. The scripting language of SAP Cloud Application Studio uses the same library for handling regular expression as ABAP).

As a prerequisite, define a business object node (WebServiceParser.Result) that represents a hierarchcial table. (In the coding below, the data type defined by this node is used in lines 10 and 11.  You do not habe to use this BO node itself, but it is required to define the data type.

node Result [0,n] {
 element ID : IntegerValue;
 element ParentID : IntegerValue;
 element Name : LANGUAGEINDEPENDENT_EXTENDED_Name;
 element Value : LANGUAGEINDEPENDENT_EXTENDED_Name;
}

The parsing works with the following ABSL code:

var json = this.Response;
var i = 0;
var i0 = 0;
var controlTag = ""; var controlTag2;
var lastControlTag;
var noOpenObjects = 0; // for checking check only
var noOpenArrays = 0; // for checking check only
var afterColon = false;
var string;
var result : elementsof WebServiceParser.Result;
var results : collectionof elementsof WebServiceParser.Result;
var ID = 0;
var parentID = 0;
while (i<json.Length()){
 i = json.FindRegex("\\{|\\}|\\[|\\]|\"|:|,|\\d+|true|false", i);
 if (i < 0) {
  raise Message.Create("E", "Parsing error (control tag not found)"); break; // *** Error ***
 }

 lastControlTag = controlTag;
 controlTag = json.Substring(i,1);
 switch (controlTag) {
 case "{" , "}", "[", "]", ","  {
  i = i + 1;
  switch (controlTag) {
  case "{" {
   noOpenObjects = noOpenObjects + 1;
   parentID = ID;
  }
  case "}" {
   noOpenObjects = noOpenObjects - 1;
  // lookup for "grandparent ID" -> parentID
   if (!(parentID == 0)) {
    var partentIDtab = results.Where(n=>n.ID == parentID);
    if (partentIDtab.Count() == 1) {
     parentID = partentIDtab.GetFirst().ParentID;
    }
    else {
     raise Message.Create("E", "Parsing error (parentID not found)"); break; // *** Error ***
    }
   }
  }
  case "[" {
   noOpenArrays = noOpenArrays + 1;
   parentID = ID;
  }
  case "]" {
   noOpenArrays = noOpenArrays - 1;
  // lookup for "grandparent ID" -> parentID
   if (!(parentID == 0)) {
    var partentIDtab = results.Where(n=>n.ID == parentID);
    if (partentIDtab.Count() == 1) {
     parentID = partentIDtab.GetFirst().ParentID;
    }
    else {
     raise Message.Create("E", "Parsing error (parentID not found for array)"); break; // *** Error ***
    }
   }
  }
  } 
  // close old node and open new one with exception list
  if ( (controlTag == "," && (lastControlTag == "}" || lastControlTag == "]")) ) {
   // no new node required
  }
  else {
   if( ID > 0 ) {results.Add(result);}
   ID = ID + 1;
   result.ID = ID;
   result.ParentID = parentID;
   result.Name = "";
   result.Value = "";
  }
  afterColon = false;
 }  // end case "{" , ",", "}"
 case ":" {
  if (afterColon) {
   raise Message.Create("E", "Parsing error (open colon)"); break; // *** Error ***
  }
  afterColon = true;
  i = i + 1;
 }
 case "\"" {
  i = i + 1;
  i0 = i;
  i = json.FindRegex("\\{|\\}|\\[|\\]|\"", i);
  if (i < 0) {
   raise Message.Create("E", "Parsing error (control tag not found after quote)"); break; // *** Error ***
  }
  controlTag2 = json.Substring(i,1);
  if (controlTag2 == "\"") {
   string = json.Substring(i0, i-i0);
   if (!afterColon) {   // before colon -> name
    result.Name = string;
   }
   else {// after colon -> value
    result.Value = string;
    afterColon = false;
   }
  }
  else {
   raise Message.Create("E", "Parsing error (quote not closed)"); break; // *** Error ***
  }
  i = i + 1;
 }
 case "t", "T" {
  if (!afterColon) {
   raise Message.Create("E", "Parsing error (true/false)"); break; // *** Error ***
  }
  result.Value = "true";
  afterColon = false;
  i = i + 4;
 }
 case "f", "F" {
  if (!afterColon) {
   raise Message.Create("E", "Parsing error (true/false)"); break; // *** Error ***
  }
  result.Value = "false";
  afterColon = false;
  i = i + 5;
 }
 default {
  // implementation for numeric (\d in regex)
  if (!afterColon) {
   raise Message.Create("E", "Parsing error (numeric before colon)"); break; // *** Error ***
  }
  i = i + 1;
  i0 = i - 1;
  i = json.FindRegex("\\{|\\}|\\[|\\]|\"|:|,|true|false", i);
  if (i < 0) {
   raise Message.Create("E", "Parsing error (numeric: end not found)"); break; // *** Error ***
  }
  controlTag2 = json.Substring(i,1);
  if (controlTag2 == "}" || controlTag2 == ",") {
   string = json.Substring(i0, i-i0);
   result.Value = string;
   afterColon = false;
  }
  else {
   raise Message.Create("E", "Parsing error (numeric end not correct)"); break; // *** Error ***
  }
 }
 }

 // check
 if (noOpenObjects < 0 || noOpenArrays < 0) {
  raise Message.Create("E", "Parsing error (error during object/array processing) (1)");  // *** Error ***
 }

}
// final check
if (!(noOpenObjects == 0) && !(noOpenArrays == 0) && !(parentID == 0)) {
 raise Message.Create("E", "Parsing error (error during final check"); // *** Error ***
}
To report this post you need to login first.

11 Comments

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

  1. Sunil Kumar Maurya

    Thank You Thomas… this post is a great help for the developer.

    could you please also give some more details on the pattern of true|false in pattern

    “\\{|\\}|\\[|\\]|\”|:|,|\\d+|true|false”.

    (0) 
      1. Ludger Bünger

        Hi Thomas,

        For completeness, I would suggest to also add the keyword ‘null’ to the regex-list as well as a respective section for ‘null’ to the token-case selection.

        But apart from this:

        Thanks for sharing, it removed the need to implement a JSON-Parser myself for the current project.

        However due to legal reasons I have to ask:

        How about copyright? May we use the code in other custom projects?

        Best regards,

        Ludger Bünger

        Custom Development with all4cloud.de

        (0) 
      1. Chris Bausch

        Hi Sunil,

        thank you for your quick reply. Do you know some developer specific documentation about which data types etc. are available or how to build a loop and so on in the scripting language of the cloud development studio?

        Thank you for your help kind regards,

        Chris

        (0) 
  2. Ludger Bünger

    Hi Thomas,

    I try to understand the code and have two questions:

    1. In line 62, you prevent creation of a new node only if the control tag is a comma. However, how do you detect the situation if there are two ending braces (e.g. {“value”: {“subvalue”:”subcontent”}} )? Currently (from my understanding) this will create an emtpy entry. Multiple levels will probably create even more entries.
    2. In line 85, you regex for {, }, [, ], and “
      Why do you also look for braces and not only for the quote?
      From my unterstanding -since you are inside a quote already- looking for braces would case issues if there is a brace as value, e.g. something like “{“.

    Best regards,

    Ludger Bünger

    Custom Development with all4cloud.de

    (0) 
    1. Thomas Schneider Post author

      Hi Ludger,

      sorry, this is long time ago and from just looking ate the code, I cannot say if you are right. I am quite sure that I tested at least the first case, but I do not remember correctly. As I not longer working for ByDesign, I cannot check this.

      Best regards,

      Thomas

      (0) 

Leave a Reply