Consuming JSON in ClearBasic
Recently I had the need to consume JSON in ClearBasic.
For example, I make a request to a web service, and the service returns a result set in JSON format. I want to display those results within Clarify.
JSON
JSON (JavaScript Object Notation) is a lightweight data-interchange format. JSON is a pretty popular data transfer format, especially for the web.
Here’s an example of a simple JSON object:
{ FirstName: Joe, LastName: SixPack, Programming Languages: [ClearBasic, Perl, Ruby] }
A real-world JSON example
I can make an HTTP request to a Google API to perform a web search. The result will be in a JSON format.
For example, a search for “fcsdk powershell” can be made by using this URL:
http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=fcsdk%20powershell
The result will be this:
{"responseData": {"results":[{"GsearchResultClass":"GwebSearch", "unescapedUrl":"http://blogs.dovetailsoftware.com/blogs/gsherman/archive/2007/04/25/using-the-fcsdk-in-powershell.aspx", "url":"http://blogs.dovetailsoftware.com/blogs/gsherman/archive/2007/04/25/using-the-fcsdk-in-powershell.aspx", "visibleUrl":"blogs.dovetailsoftware.com", "cacheUrl":"http://www.google.com/search?qu003dcache:u9RLwKjiIPYJ:blogs.dovetailsoftware.com", "title":"Gary Sherman : Using the u003cbu003efcSDKu003c/bu003e in u003cbu003ePowerShellu003c/bu003e", "titleNoFormatting":"Gary Sherman : Using the fcSDK in PowerShell", "content":"Apr 25, 2007 u003cbu003e...u003c/bu003e Years ago, if I wanted to u0026quot;scriptu0026quot; Clarify , I would use UNIX shell scripts, including UNIX mini languages such as sed and awk, u003cbu003e...u003c/bu003e"}, {"GsearchResultClass":"GwebSearch", "unescapedUrl": "http://blogs.dovetailsoftware.com/blogs/gsherman/archive/2007/05/14/configuring-logging-in-the-fcsdk-without-a-config-file.aspx", "url":"http://blogs.dovetailsoftware.com/blogs/gsherman/archive/2007/05/14/configuring-logging-in-the-fcsdk-without-a-config-file.aspx", "visibleUrl":"blogs.dovetailsoftware.com", "cacheUrl":"http://www.google.com/search?qu003dcache:VNz7QDYq4l4J:blogs.dovetailsoftware.com", "title":"Gary Sherman : Configuring logging in the u003cbu003efcSDKu003c/bu003e without a config file", "titleNoFormatting":"Gary Sherman : Configuring logging in the fcSDK without a config file", "content":"May 14, 2007 u003cbu003e...u003c/bu003e This same scenario will apply not only to u003cbu003ePowerShellu003c/bu003e scripts, but to any .NET app (such as C# or VB.NET) that uses the u003cbu003efcSDKu003c/bu003e. u003cbu003e...u003c/bu003e"}], "cursor":{"pages":[{"start":"0","label":1}],"estimatedResultCount":"2","currentPageIndex":0, "moreResultsUrl":"http://www.google.com/search?oeu003dutf8u0026ieu003dutf8u0026sourceu003dudsu0026startu003d0u0026hlu003denu0026qu003dfcsdk+powershell"}}, "responseDetails": null, "responseStatus": 200}
It may look a little scary, but its really not once you break it down.
There’s an object called responseData, and it has a properties such as estimatedResultCount, responseStatus, and results. results is actually an array of objects. Each of these objects has its own properties, such as url, title and visibleUrl,
I want to be able to parse that in ClearBasic and do something with that data – for example, show it in a grid or a some other type of list.
HTTP Request
First, lets make the HTTP request to get the string of data back.
Function GetHTTPResponse (url As String, method As String, data As String) as String Dim oHttp as Object Set oHttp = CreateObject("microsoft.XmlHttp") oHttp.open method, url, False oHttp.send ByVal data GetHTTPResponse = oHttp.ResponseText End Function Dim url as String Dim terms as String Dim json as String terms = "dovetail software" terms = "fcsdk powershell" url = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=" & terms json = GetHTTPResponse (url,"GET", "")
Turn the JSON string into an object
Next, we’ll turn that big ugly string of data into an object.
Typically, I would use a JSON parser. The JSON website has links to parsers in a boatload of languages. Not surprisingly, Clarify’s proprietary ClearBasic languages isn’t one of them. But that’s OK, because JavaScript can do this for us.
To convert a JSON text into an object, you can use the eval() function in JavaScript. eval() invokes the JavaScript compiler. Since JSON is a proper subset of JavaScript, the compiler will correctly parse the text and produce an object structure.
But how can we call JavaScript from within ClearBasic?
We’ll use the Microsoft Script Control. This gives us access to the Microsoft scripting engine, including JScript.
Set scriptControl = CreateObject("MSScriptControl.ScriptControl") scriptControl.Language = "JScript" Set searchResultObject = scriptControl.Eval("(" + json + ")")
The code creates a ScriptControl object, sets its language to JScript, and then does an eval() on our json, which gives us back a proper object.
We can then access the properties of the object, such as the responseStatus:
Debug.Print "Search Response Status: " & searchResultObject.responseStatus
Accessing an array within an object
This is where things get a little tricky. I would like to simply put the searchResultObject.results array into a ClearBasic array, but this doesn’t work. So, I’ll inject some Javascript functions into the ScriptControl , and then pull the data out of the ScriptControl via primitive datatypes (string, integer, etc.).
A JavaScript function to get the number of results
Here’s the Javascript that I want:
function getNumberOfResults (searchResultObject) { return searchResultObject.responseData.results.length; }
To “inject” that function into the ScriptControl, I put the function into a string, and then call on the AddCode method:
Sub AddFunctionToScriptObject(scriptControl, functionDefinition) scriptControl.AddCode functionDefinition End Sub Dim getNumberOfResultsFunction As String getNumberOfResultsFunction = "function getNumberOfResults(searchResultObject){ return searchResultObject.responseData.results.length;}" AddFunctionToScriptObject scriptControl, getNumberOfResultsFunction
Now, I can call on this function to get the number of results
numberOfResults = scriptControl.Run("getNumberOfResults", searchResultObject) Debug.Print "Number of Search Results: " & CStr(numberOfResults)
Getting a property of an object within an array
Similarly, I’ll inject a function to return a property of one result object.
getResultByIndexFunction = "function getPropertValueForResultByIndex (searchResultObject, index, prop){ return searchResultObject.responseData.results[index][prop];}" AddFunctionToScriptObject scriptControl, getResultByIndexFunction
Notice that the index of the results array, and the property to return, are inputs to the function.
I can now get the url of the first result (index = 0) like this:
URL = scriptControl.Run("getPropertValueForResultByIndex", resultObject, 0, "url")
That’s all the hard part. Now I can rinse, lather, repeat for all the other properties, and I can loop over each result.
Rinse, Lather, Repeat
For convenience, I’ve created a user-defined type for a search result:
Type SearchResult Url as String Id as String Title as String Content as String End Type
A subroutine to populate a Search Result object:
Sub getSearchResultByIndex(scriptControl as Object, resultObject as Object, index as Integer) oneResult.Title = scriptControl.Run("getPropertValueForResultByIndex", resultObject, index, "titleNoFormatting") oneResult.Content = scriptControl.Run("getPropertValueForResultByIndex", resultObject, index, "content") oneResult.Id = index oneResult.URL = scriptControl.Run("getPropertValueForResultByIndex", resultObject, index, "url") End Sub
And a loop to get and print each result:
For index = 0 to numberOfResults - 1 getSearchResultByIndex scriptControl, searchResultObject, index Debug.Print "" Debug.Print "Search Result # " & cstr(index) Debug.Print "Title: " & oneResult.Title Debug.Print "URL: " & oneResult.Url Debug.Print "" Next index
An example of the output (using cbbatch as the ClearBasic engine):
And there you have it – from ClearBasic, we’ve made an HTTP request to Google, performed a search which returned a result set in JSON, parsed the JSON, and got access to all that data from within CB.
Code
The complete code example is available on Pastie.
Summary
Now that you know how to consume JSON from ClearBasic, you can consume data from all sorts of sources, including many web services.
Rock on.