Rest calls and JSON Parsing in LotusScript

This is my first technical article on this blog. To reach a broader audience in the Notes/Domino community (and to avoid weird translations), I decided to write technical articles in English. Other experiences and opinions on the Notes/Domino world will stay in Dutch.

That being said, I wanted to discuss the new functions in LotusScript to do HTTP Requests and parsing the json response to use it in a Notes application.

You can use the new NotesHTTPRequest class to do the api request. This class allows you to do get, put and post requests. You can set headers using the setHeaderField method.
By default, the returned response can be either a byte array or a string (depending on the service called). To avoid this, set the (undocumented) preferString property to true. This will enforce the response to be a string.

Dim webRequest As NotesHTTPRequest
Set webRequest = session.createhttprequest()

'Get the response Dim response As String
webrequest.preferstrings = True response = webrequest.Get(<URL>)
'Throw error if response status is not OK If InStr(webRequest.Responsecode, "200 OK") = 0 Then 'Return Status is not OK Error 1000, "Request returned response code " + webRequest.responseCode End If

Note that the URL that you pass to the request has to be encoded. Most browsers encode the url after you typed it in the addressbar. But the NotesHTTPRequest does not. Therefor, make sure you encode the url before using it for the request. Here’s a small example:

Dim url as string
url = "https://mysite.com/get?name=John Doe"

'Using this url in the get request won't work
response = webrequest.get(baseurl + parameters)

'By encoding the url, it will
response = webrequest.get(urlEncode(url))

%REM
I use this function to encode url parts
It will encode special characters, but won't touch & or =, 
so you can pass the full url in one go
Kuddos to 
http://dominonotes.blogspot.com/2008/11/lotusscript-equivalents-for-urlencode.html
%END REM
Function urlEncode(s As String) As String
    If Len(s) = 0 Then Exit Function

    Dim tmp As String
    Dim c As String
    Dim i As Integer

    For i = 1 To Len(s)
        c = Mid(s, i, 1)
        If (Asc(c) >= 65 And Asc(c) <= 90) _
            Or (Asc(c) >= 97 And Asc(c) <= 122) _
            Or (Asc(c) >= 48 And Asc(c) <= 58) _
            Or Asc(c) = 38 _
            Or (Asc(c) >= 45 And Asc(c) <= 47) _
            Or Asc(c) = 58 Or Asc(c) = 61 _
            Or Asc(c) = 63 Or Asc(c) = 126 Then
            tmp = tmp + c
        Else
            tmp = tmp + "%" + Hex(Asc(c))
        End If
    Next i
    urlEncode = tmp
End Function

You can now parse the response using the NotesJSONNavigator class. However, this still has some caveats. The new class doesn’t behave well on some special characters in the json you pass (like CR, LF, °, ü, …) and you’ll get an error trying to parse this. According to HCL, there might still be an issue with the character encoding while processing the json data.

However, there is a workaround to avoid this by forcing the data to UTF-8. When you save the response to a file and then read it using a notesstream with UTF-8 encoding, you will be able to parse the data (Thanks to Dave Cohen for the tip)

'write the requests response to a file
Dim fileNum As Integer
fileNum% = FreeFile()
Dim filepath As String
filepath = "c:\temp\" + Format(Now, "yyyy-mm-dd-hhnnss") + ".json"
Open filepath For Output As fileNum%
Print #filenum, response
Close filenum

'Read the file using a NotesStream
Dim inbuf As NotesStream
Dim json As variant
Set inbuf = session.Createstream()
If Not(inbuf.Open(filepath, "UTF-8")) Then
	Error 1001, "Unable to open JSON file (" + filepath + ")"
End If
json = inbuf.Read()
Call inbuf.Close()

'remove temporary file
Kill filepath

'Create the Json Navigator to parse the data
Dim jsnav As NotesJSONNavigator
If IsArray(json) Then
	Set jsnav = session.CreateJSONNavigator(json)
Else
	Error 1002, "JSON is nothing"
End If

Once you have the NotesJSONNavigator object, you can loop through the different elements. Here’s some sample code to do so.

Dim parsed As string
Dim el As notesjsonelement
Set el = jsnav.Getfirstelement()

While Not el Is Nothing
	parsed = parsed + parseelement(el, "")
	Set el = jsnav.Getnextelement()		
Wend

MsgBox parsed


Function parseElement(el As notesjsonelement, parent As string)
	Dim returnValue As String
	Dim sName As String
	sName = parent + el.name
	Select Case el.type
		Case 1: 'Object
			Dim ob As notesjsonobject
			Set ob = el.value
			returnValue = returnValue + parseObject(ob, parent + el.name)
		Case 2: 'Array
			Dim ar As NOTESJSONARRAY
			Set ar = el.value
			returnValue = returnValue + parseArray(ar, parent + el.name)
		Case 3: 'String
			returnValue = returnValue +  sName + " - '" & el.Value & "'" + Chr$(10)
		Case 4: 'Number
			returnValue = returnValue +  sName + " - " & el.value + Chr$(10)
		Case 5: 'Boolean
			Dim b As String
			If el.value Then 
				returnValue = returnValue +  sName + " - true" + Chr$(10)
			Else
				returnValue = returnValue +  sName + " - false" + Chr$(10)
			End If
		Case 64: 'empty
			returnValue = returnValue +  sName + " - <EMPTY>" + Chr$(10)
		Case Else: 'Unknown
			returnValue = returnValue +  sName + " Unknown type " & el.type + Chr$(10)
	End Select
	parseElement = returnValue
End Function

Function parseObject(ob As notesjsonobject, parent As String) As string
	Dim el As notesjsonelement
	Dim sName As String
	Dim returnValue As string
	Set el = ob.Getfirstelement()
	While Not el Is Nothing
		returnValue = returnValue + parseElement(el, parent + ".")
		Set el = ob.Getnextelement()
	Wend
	parseObject = returnValue
End Function

Function parseArray(ar As NOTESJSONARRAY, parent As String)
	Dim el As notesjsonelement
	Dim sName As String
	Dim returnValue As String
	
	
	Set el = ar.Getfirstelement()
	Dim counter As Integer
	While Not el Is Nothing
		returnValue = returnValue + parseElement(el, parent + "." & counter)
		Set el = ar.Getnextelement()
		counter = counter + 1
	Wend
	parseArray = returnValue
End Function

Another way to get a json element is to get it by name or by pointer. A
JSON Pointer defines a string syntax for identifying a specific value within a JavaScript Object Notation (JSON) document.

'By name
Set el = jsnav.Getelementbyname("latitude")

'By pointer
Set el = jsnav.getElementbypointer("/daily/data/0")

I created a demo application that allows youto retrieve a weather forecast for any place on earth. I used the Google Maps API to retrieve address information and the Dark Sky API to get a forecast. You’ll need a key for both api to be able to use the app. You can download the demo here.

Advertenties

Geef een reactie

Vul je gegevens in of klik op een icoon om in te loggen.

WordPress.com logo

Je reageert onder je WordPress.com account. Log uit /  Bijwerken )

Google+ photo

Je reageert onder je Google+ account. Log uit /  Bijwerken )

Twitter-afbeelding

Je reageert onder je Twitter account. Log uit /  Bijwerken )

Facebook foto

Je reageert onder je Facebook account. Log uit /  Bijwerken )

Verbinden met %s