How to Parse a String in JSON Format in ABSL
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 ***
}
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".
In JSON, you specify boolean values as true or false w/o quotation marks.
The FindRegex function finds these.
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
Thank you Thomas, this is a great help for us.
Hi Thomas,
in my implementation WebServiceParser is not found. Which import do I have to include?
Hi Chris
WebServiceParser is custom BO, I think Thomas use this name only for example purpose.
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
Chris,
You can find the documents in https://help.sap.com/studio_cloud
you can download and read online version
Hi Thomas,
I try to understand the code and have two questions:
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
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
.
Thank you Thomas ,
Great job!
Best wishes,
Aleksei Belousov
Hello,
Can you please brief on where the second code is attached ? Also Response , message etc are throwing errors.
Thank you Thomas, this is a great help for developers.
Hello Experts,
Please help me
https://answers.sap.com/questions/13460987/how-to-use-regular-expression-in-find-and-replace.html
Thanks in advance,
Ananth