Coding Corner: Json BDL API Sample and JsonPath Alternative

Micro Focus Expert
Micro Focus Expert
3 5 10.7K

Introduction

A short while ago, a couple of  Silk Performer users posted the following question in the forum: How do I use Json effectively in a BDL script? They were looking for ...

  • a comprehensive Json example and for
  • support for an XPath-like Json API

I've put together a short example, which hopefully answers some questions around Json and Silk Performer. Let's grab the following Json example text and get started:

const SOME_JSON := ""
"  { \"store\": {"
"    \"book\": [ "
"      { \"category\": \"reference\","
"        \"author\": \"Nigel Rees\","
"        \"title\": \"Sayings of the Century\","
"        \"price\": 8.95"
"      },"
"      { \"category\": \"fiction\","
"        \"author\": \"Evelyn Waugh\","
"        \"title\": \"Sword of Honour\","
"        \"price\": 12.99,"
"        \"isbn\": \"0-553-21311-3\""
"      }"
"    ],"
"    \"bicycle\": {"
"      \"color\": \"red\","
"      \"price\": 19.95"
"    }"
"  }"
"}   ";

The question now is: How do I quickly get the name of the second author (Evelyn Waugh)?

Using BDL API

To solve this problem with the help of the Json BDL API, I need to include the json.bdh file:

use "json.bdh"

Next, I have to script the transaction:

  transaction TPureBdl
  var sResult : string;
    hJsonRoot, hJsonStore, hJsonBookArray, hJsonBook : number;
  begin
    hJsonRoot := JsonParse(SOME_JSON);
    // under the root element, there is a property 'store', which is an object
    JsonGetObjectProperty(hJsonRoot, "store", hJsonStore);
    // get the property 'book' which is an array
    JsonGetArrayProperty(hJsonStore, "book", hJsonBookArray);
    // get the 2nd array element (zero-based index), which is an object
    JsonArrayGetObjectElement(hJsonBookArray, 1, hJsonBook); 
    // get the property 'author', which is a string
    JsonGetStringProperty(hJsonBook, "author", sResult);     
    Print("2nd author is: "+sResult);
  end TPureBdl;

Using JsonPath as third-party JAVA API

In the previous example, I needed four lines of BDL code to get the name of the author. However, there's also a solution, which requires just a single line of code. To achive this, I'm going to use a JAVA implementation of JsonPath.

The JsonPath project is available on http://code.google.com/p/json-path/downloads/list.

First, I'm going to download the three .jar files and add them as Data Files to my Silk Performer project.

Then, I create a new JAVA class and write a short conveniece wrapper over the JsonPath API.

import com.jayway.jsonpath.*;

public class JsonPathWrp
{
  public static String read(String json, String path)
  {
    return JsonPath.read(json, path);
  }
}

Now I need to include the java.bdh file ...

use "java.bdh"

... and start a JAVA virtual machine in the TInit transaction:

  transaction TInit
  begin     
    JavaCreateJavaVM();
  end TInit;


Finally, I call my convinience class from the transaction. With the help of the JsonPath API we can get the result in one operation.

  transaction TJsonPath
  var sResult : string;
  begin
    JavaSetString(JAVA_STATIC_METHOD, SOME_JSON);
    JavaSetString(JAVA_STATIC_METHOD, "$.store.book[1].author");
    JavaCallMethod(JAVA_STATIC_METHOD, "JsonPathWrp.read");
    JavaGetString(JAVA_STATIC_METHOD, sResult);
    Print("2nd author is: "+sResult);
  end TJsonPath;

The memory overhead for loading a JVM and the necessary .jar files is about 40 MB. If per default 50 users are sharing a JVM, this is about 820 KB per virtual user.

I hope these examples make it a bit clearer to you how to effectively use the Json BDL API in Silk Performer. If you have any further questions, feel free to leave a comment or post another question in the Silk Performer forum. For more information on Json, you can also refer to the comprehensive Json Functions Reference in the Silk Performer documentation.

Tags (1)
5 Comments
kannan3 Absent Member.
Absent Member.

HI..  Using SilkPeformer 16.0, I've tried to use the  above example as a template for a  POST operation to a RESTFUL service.   I'm  getting an invalid Json format when I do a Try script.

Are there any configs to be set when using JSON data??

-kannan

AnsumanP Absent Member.
Absent Member.

Hi Johannes, I am trying  to implement the above java  code. I parsed the JSON as  string and it works fine. According to the  README.md (github.com/.../JsonPath)  , If we are trying too parse multiple  property from the same JSON,  we need to use the  JSON object

Object document = Configuration.defaultConfiguration().jsonProvider().parse(json);

But  to  capture the above  object document in silk Method call,  we need to pass the object reference  in the JavaCallMethod(). If you can provide some example on this ,it will be helpful.

Micro Focus Expert
Micro Focus Expert

@AnsumanP: not sure if I can format the answer here properly, but I will try;  if it is unclear, feel free to send me an email instead

1) in the Java code I e.g. overload the read method; if I am just pass a json String here but no path for a query, I will return a ReadObject reference for later reuse

2) in the Java code I add e.g. a readString and readStringArray method

3) in the BDL code, if I expect a single result, I use readString

4) in the BDL code, if I expect a list as result, I use readStringArray

import java.util.ArrayList;

import java.util.List;

import com.jayway.jsonpath.*;

public class JsonPathWrp

{

 public static String read(String json, String path)

 {

   return JsonPath.read(json, path);

 }

 public static ReadContext read(String json)

 {

   return JsonPath.parse(json);

 }

 public static String readString(ReadContext context, String path)

 {

  return context.read(path);

 }

 public static List<String> readStringArray(ReadContext context, String path)

 {

   Object retVal = context.read(path);

   if (retVal instanceof String)

   {

     List<String> list = new ArrayList<String>();

     list.add((String)retVal);

     return list;

   }

   else

   {

     return (List<String>)retVal;

   }

 }

}

///// /////

// BDL

///// /////

 transaction TJsonPathEx

 var sResult : string;

   hJsonObj, hListObj : number;

   size,i :number;

 begin

   // create Json object    

   JavaSetString(JAVA_STATIC_METHOD, SOME_JSON);

   JavaCallMethod(JAVA_STATIC_METHOD, "JsonPathWrp.read");

   hJsonObj := JavaGetObject(JAVA_STATIC_METHOD);

   // Sample 1: read author of 2nd book

   JavaSetObject(JAVA_STATIC_METHOD, hJsonObj);

   JavaSetString(JAVA_STATIC_METHOD, "$.store.book[1].author");

   JavaCallMethod(JAVA_STATIC_METHOD, "JsonPathWrp.readString");

   JavaGetString(JAVA_STATIC_METHOD, sResult);

   Print("2nd author is: "+sResult);

   // Sample 2: get all authors where price of book is >5

   JavaSetObject(JAVA_STATIC_METHOD, hJsonObj);

   JavaSetString(JAVA_STATIC_METHOD, "$.store.book[?(@.price > 5)].author");

   JavaCallMethod(JAVA_STATIC_METHOD, "JsonPathWrp.readStringArray");

   hListObj := JavaGetObject(JAVA_STATIC_METHOD); // List<String>

   JavaCallMethod(hListObj, "size");

   size := JavaGetNumber(hListObj);

   for i := 0 to size-1 do

     JavaSetNumber(hListObj, i);

     JavaCallMethod(hListObj, "get");

     JavaGetString(hListObj, sResult);

     Print("author with prize > 5 : "+sResult);

   end;

 end TJsonPathEx;

AnsumanP Absent Member.
Absent Member.

Thanks Johannes. I tried the //Sample 1 and perfectly works!!

I updated the above code to handle diffrent types of return for my UseCase (Not sure if there is better way to handle it,Please suggest)

import java.util.List;

import com.jayway.jsonpath.*;

public class JsonPathWrp

{

public static String readString(String json, String path,String type)

 {      

   switch(type)

   {

   case "STR" :    

     return  JsonPath.read(json, path);        

   case "INT" :    

     int Property =   JsonPath.read(json, path);                    

     return Integer.toString(Property);

   case "LONG" :

     long Property1 =   context.read(path);  

     return Long.toString(Property1);                            

   case "FLT" :

     float Property2 =   context.read(path);        

     return Float.toString(Property2);                                

   case "DBL" :      

     double Property3 =   context.read(path);

     return Double.toString(Property3);            

   case "CHR" :  

     char Property4 =  context.read(path);  

     return Character.toString(Property4);      

   default :

      return context.read(path);        

   }      

}

public static ReadContext read(String json)

{

  return JsonPath.parse(json);

}

public static String readString(ReadContext context, String path,String type)

 {      

   switch(type)

   {

   case "STR" :          

     return context.read(path);

   case "INT" :    

     int Property =   context.read(path);                      

     return Integer.toString(Property);  

   case "LONG" :

     long Property1 =   context.read(path);  

     return Long.toString(Property1);                            

   case "FLT" :

     float Property2 =   context.read(path);        

     return Float.toString(Property2);                              

   case "DBL" :      

     double Property3 =   context.read(path);

     return Double.toString(Property3);            

   case "CHR" :  

     char Property4 =  context.read(path);  

     return Character.toString(Property4);      

   default :

      return context.read(path);        

   }      

}

}

transaction TJsonPathEx

var sResult : string;

  hJsonObj  : number;

begin

  // create Json object    

  JavaSetString(JAVA_STATIC_METHOD, SOME_JSON);

  JavaCallMethod(JAVA_STATIC_METHOD, "JsonPathWrp.read");

  hJsonObj := JavaGetObject(JAVA_STATIC_METHOD);

  // Sample 1: read author of 2nd book

  JavaSetObject(JAVA_STATIC_METHOD, hJsonObj);

  JavaSetString(JAVA_STATIC_METHOD, "$.store.book[1].author");

  //Provide the Type of Return you are expecting: STR,INT,LONG,FLT,DBL,CHR  

  JavaSetString(JAVA_STATIC_METHOD, "STR");  

  JavaCallMethod(JAVA_STATIC_METHOD, "JsonPathWrp.readString");

  JavaGetString(JAVA_STATIC_METHOD, sResult);

end TJsonPathEx;

Micro Focus Expert
Micro Focus Expert

@AnsumanP: Your sample isn't the prettiest, but it should work. Please add following line(s) at the end of the BDL transaction to avoid a memory leak:

add at the end of my example to avoid memory leaks:

   JavaFreeObject(hJsonObj);

   JavaFreeObject(hListObj);

for your example add to avoid a memory leak:

 JavaFreeObject(hJsonObj);

If you have more questions, please open a support ticket referencing this blog here.

The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.