How to pass request through HTTP " GET " statement - vba

I'm trying to fetch data from a currency api. I'm fairly new to VBA and very new to JSON, but I can't figure out how to pass a request through the HTTP.... I can fetch any data I want that does not require a request though to pin it down.
The API is here: https://www.bitfinex.com/pages/api
I'm trying to pass a timestamp(time) request. See instructions ( pasted form their website just below )
Trades
GET /trades/:symbol
Get a list of the most recent trades for the given symbol.
Request
timestamp (time): Optional. Only show trades at or after this timestamp.
limit_trades (int): Optional. Limit the number of trades returned. Must be >= 1. Default is 50.
Response
An array of dictionaries:
tid (integer)
timestamp (time)
price (price)
amount (decimal)
exchange (string)
type (string) "sell" or "buy" (can be "" if undetermined)
My code so far
Private Function get_price() As String
Dim xml_a As Object
Set xml_a = CreateObject("MSXML2.XMLHTTP")
With xml_a
.Open "Get", "https://api.bitfinex.com/v1/trades/BTCUSD", False
'' <<< something should be written here right? I just don't know what... >>>''
.send
get_price = .responseText
End With
Set xml_a = Nothing
End Function
Sub tester()
Dim JSON_return As Object
Set JSON_return = JSON.parse(get_price())
Debug.Print JSON_return.Item("last_price")
End Sub

Related

NotesJsonNavigator and international characters

I have json data that I want to use in a lotusscript library.
I use NotesJsonNavigator to navigate through the data.
If I set preferUTF8 to false, the values in NotesJSONElements are strings, however it drops international characters like ö, å, ...
If I set the parameter preferUTF8 to true, the values in my NotesJSONElements are byte arrays.
How can I convert these byte arrays into Strings, taking international characters into account?
Example:
Dim session As New NotesSession
'URL Parameters have to be UTF-8 encoded.
Dim url As string
URL = "https://maps.googleapis.com/maps/api/geocode/json?address=Malm%C3%B6%2C%20Sweden&key=<My Google API Key>"
'Creating the request
Dim webRequest As NotesHTTPRequest
Dim response As Variant
Set webRequest = session.createhttprequest()
'Get response as byte array
webrequest.preferstrings = False
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
'Create the JSON NAVIGATOR
Dim jsnav As NotesJSONNavigator
If Not IsArray(response) Then Error 1000, "JSON is nothing"
Set jsnav = session.CreateJSONNavigator(response)
'Get data as Strings
jsnav.Preferutf8 = False
'Declaring json specific elements
Dim el_address As NotesJSONElement
Dim el_state As NotesJSONElement
'Retrieving the address...
Set el_address = jsnav.getelementbypointer("/results/0/formatted_address")
Print el_address.value
'Retrieving state
Set el_state = jsnav.getelementbypointer("/results/0/address_components/1/long_name")
Print el_state.value
This prints
Malm, Sweden
Skne
This is going to be fixed in the next 10.0.1 FP. Keep an eye out for DCONB8F6JV in the fix list.

Parsing XML Response in VBA and extracting only last data

I'm trying to do a XML request through excel vba for one of the internal links (in our company). when i send request and receive response using the code below, i get the following as response text:
[{"CPN":"700-42887-01","ExtractDt":"2018-04-02
00:00:00","Demand":"8645"},"CPN":"700-42887-01","ExtractDt":"2018-04-09
00:00:00","Demand":"8985"},{"CPN":"700-42887-01","ExtractDt":"2018-04-16
00:00:00","Demand":"9341"},{"CPN":"700-42887-01","ExtractDt":"2018-04-23
00:00:00","Demand":"9589"},{"CPN":"700-42887-01","ExtractDt":"2018-04-30
00:00:00","Demand":"9210"},{"CPN":"700-42887-01","ExtractDt":"2018-05-07
00:00:00","Demand":"9698"},{"CPN":"700-42887-01","ExtractDt":"2018-05-14
00:00:00","Demand":"9542"},{"CPN":"700-42887-01","ExtractDt":"2018-05-21
00:00:00","Demand":"9692"},{"CPN":"700-42887-01","ExtractDt":"2018-05-28
00:00:00","Demand":"10416"},{"CPN":"700-42887-01","ExtractDt":"2018-06-04
00:00:00","Demand":"6777"},{"CPN":"700-42887-01","ExtractDt":"2018-06-11
00:00:00","Demand":"12774"},{"CPN":"700-42887-01","ExtractDt":"2018-06-18
00:00:00","Demand":"12912"},{"CPN":"700-42887-01","ExtractDt":"2018-06-25
00:00:00","Demand":"12693"},{"CPN":"700-42887-01","ExtractDt":"2018-07-02
00:00:00","Demand":"12895"},{"CPN":"700-42887-01","ExtractDt":"2018-07-09
00:00:00","Demand":"13366"},{"CPN":"700-42887-01","ExtractDt":"2018-07-16
00:00:00","Demand":"13550"},{"CPN":"700-42887-01","ExtractDt":"2018-07-23
00:00:00","Demand":"7971"},{"CPN":"700-42887-01","ExtractDt":"2018-07-30
00:00:00","Demand":"12442"},{"CPN":"700-42887-01","ExtractDt":"2018-08-06
00:00:00","Demand":"12960"},{"CPN":"700-42887-01","ExtractDt":"2018-08-13
00:00:00","Demand":"14106"},{"CPN":"700-42887-01","ExtractDt":"2018-08-20
00:00:00","Demand":"13543"},{"CPN":"700-42887-01","ExtractDt":"2018-08-27
00:00:00","Demand":"13570"},{"CPN":"700-42887-01","ExtractDt":"2018-09-03
00:00:00","Demand":"13506"},{"CPN":"700-42887-01","ExtractDt":"2018-09-10
00:00:00","Demand":"13914"},{"CPN":"700-42887-01","ExtractDt":"2018-09-17
00:00:00","Demand":"13241"},{"CPN":"700-42887-01","ExtractDt":"2018-09-24
00:00:00","Demand":"13449"}]
I want to extract only the last Value - Namely 13449. What is the code that i need to write to accomplish this.
Thanks in Advance!`
Code used
Sub xmlparsing()
Dim jstring As String
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "**INTERNAL COMPANY LINK HERE**", False
.send
If .Status <> 200 Then Exit Sub
jstring = .responseText
Debug.Print jstring
End With`
End Sub
You could use InStrRev
Mid$(responseText, InStrRev(responseText, ":") + 2, (InStrRev(responseText, "}") - 1) - (InStrRev(responseText, ":") + 2))
InStrRev walks the string from right to left. We know you want the value at the end of the string so this direction is useful. We specify as an argument the character to find. The overall string is the responseText.
The first character to find is ":", from right to left. This will be where you have :"13449"}]. Offset from this + 2 to get the actual start of the value you want, in this case the 1 in 13449.
Same logic to determine end point of string. I use "}" as end point then make an adjustment to move forward to the numbers. Mid allows you to specify a string, start point and number of characters. I pass the arguments to extract the required string to Mid. I used typed functions (with the $ at the end) as more efficient when working with strings.
Considering the fact, that you have already parsed the XML to string, then the easiest fact is to try to slice the string. To see how it works, put the string from .responseText to A1 range and run this:
Sub TestMe()
Dim responseText As String
responseText = Range("A1")
Dim myArr As Variant
myArr = Split(responseText, "Demand"":""")
Debug.Print Left(myArr(UBound(myArr)), Len(myArr(UBound(myArr))) - 4)
End Sub
What it does is to split the string into array by the word Demand":" and to take anything but the last 4 characters of the last unit of the array.

Formula Written in Module Producing #VALUE

With assistance from outside sources I have this code that takes latitude and longitude and extracts zipcodes. Here is the code:
Public Function ReverseGeoCode(myInput1 As String, myInput2 As String) As String
'You will need to reference Microsoft XML, v6.0 object library
Dim XMLDoc As New DOMDocument60
Dim XMLNODE As IXMLDOMNode
Dim I As Long
Dim lat, lng, myAddress, myZipcode, reportZipcode As String
Dim splitAddress, splitZipcode As Variant
lat = myInput1
lng = myInput2
XMLDoc.Load "http://maps.googleapis.com/maps/api/geocode/xml?latlng=" & lat & "," & lng & " &sensor=false"
Do Until XMLDoc.readyState = 4
DoEvents
Loop
If Len(XMLDoc.Text) = 0 Then
Call MsgBox("No Data!")
Exit Function
End If
Set XMLNode = XMLDoc.SelectSingleNode("/GeocodeResponse/result/formatted_address")
For i= 0 To XMLNode.ChildNodes.Length - 1
myAddress = XMLNode.ChildNodes(i).Text
Next i
splitAddress = Split(myAddress, ",")
myZipcode = splitAddress(2)
myZipcode = Trim(myZipcode)
splitZipcode = Split(myZipcode, " ")
reportZipcode = splitZipcode(1)
reportZipcode = Trim(reportZipcode)
ReverseGeoCode = reportZipcode
End Function
So the code works, and I know it might not be the cleanest. But the issue is when I call it in the Excel sheet using "=ReverseGeoCode(Cell1, Cell2)". Sometimes it works fine, other times it produces the return "#VALUE!" and I am not entirely sure why. I attached an image below to show you an example of the error. Does anyone have an idea of why this error is producing?
General observations:
So to pick up on what I wrote in the comments, here is an outline.
You don't want to use an User Defined Function. This will keep on making repeated calls. You definitely risk hitting a call limit to the API without an API key, and possibly with; it is inefficient and it is not necessary. Instead, write a sub which you call once and which loops all the required cells in the sheet and issues the API calls and returns the zip codes. An API key is a method of authentication used with many API calls. You shouldn't share it by the way.
These repeated calls, possibly hitting a limit and the fact that UDFs are frequently calculated maybe the source of your woes.
With efficiency in mind, first remove duplicates from the sheet to avoid calls that are not required. Switch of Screen-Updating and anything else e.g. CalculationMode to manual whilst performing.
From what I have read you require an API key once you have hit a daily limit. Not sure what the API limit is for free version or without API key.
Outline code (XML request with some psuedo code):
Option Explicit
Public Sub ListZipCodes()
Dim lat As Double, longitude As Double
Const APIKEY As String = "yourAPIkey"
Application.ScreenUpdating = False '<==Speed up code when actually working with sheet
'Code to remove duplicates
'Code to loop sheet and call function on each input set of values
'Example call. These would be picked up from cells
lat = 40.714224
longitude = -73.961452
Debug.Print GetZipCode(lat, longitude, APIKEY)
Application.ScreenUpdating = True
End Sub
Public Function GetZipCode(ByVal lat As Double, ByVal longitude As Double, ByVal APIKEY As String) As String
Dim sResponse As String
With CreateObject("MSXML2.XMLHTTP")
Dim URL As String
URL = "https://maps.googleapis.com/maps/api/geocode/xml?latlng=" & lat & "," & longitude & "&key=" & APIKEY
.Open "GET", URL, False
.send
If .Status <> 200 Then
GetZipCode = "API call failed"
Exit Function
End If
Dim XMLDoc As New DOMDocument60, XMLNODE As IXMLDOMNode
XMLDoc.Load .responseBody
If Len(XMLDoc.Text) = 0 Then GetZipCode = "No data"
Set XMLNODE = XMLDoc.SelectSingleNode("/GeocodeResponse/result/formatted_address")
GetZipCode = Split(Trim$(Split(XMLNODE.Text, Chr$(44))(2)), Chr$(32))(1)
End With
End Function
Requesting JSON rather than XML response:
The reason calling as JSON was falling over was that the response needed to be decoded. Here is the function re-written to handle a JSON response.
This requires the download of JSONConverter, which you then import and add a reference to Microsoft Scripting Runtime via VBE > Tools > References.
The example below was run with
latitude: 42.9865913391113,
longitude: -100.137954711914
VBA:
Public Function GetZipCode(ByVal lat As Double, ByVal longitude As Double, ByVal APIKEY As String) As String
Dim sResponse As String, json As Object
With CreateObject("MSXML2.XMLHTTP")
Dim URL As String, formattedAddress As String
URL = "https://maps.googleapis.com/maps/api/geocode/json?latlng=" & lat & "," & longitude & "&key=" & APIKEY
.Open "GET", URL, False
.send
If .Status <> 200 Then
GetZipCode = "API call failed"
Exit Function
End If
Set json = JsonConverter.ParseJson(StrConv(.responseBody, vbUnicode))
formattedAddress = json("results").item(1)("formatted_address")
GetZipCode = Split(Trim$(Split(formattedAddress, Chr$(44))(2)), Chr$(32))(1)
End With
End Function
With a JSON request the initial object you get back is a dictionary, as denoted by the opening "{" in the decoded response:
i.e. Set json = JsonConverter.ParseJson(StrConv(.responseBody, vbUnicode)) returns a dictionary object
The data of interest, in the dictionary, has the key "results", as you may observe from the above.
This can be accessed with json("results"), which returns a collection of dictionaries. This being denoted by the following "[", for collection, and subsequently by the start of the first dictionary within the collection, indicated again by "{".
I can grab the first dictionary in the collection by index with:
json("results").item(1)
An inspection of the keys in this dictionary shows that one of the keys is what we are after i.e."formatted_address".
It's associated value is a primitive datatype; in this case a string. This means we can directly access it using the key (a further object is not returned).
formattedAddress = json("results").item(1)("formatted_address")
Now that we have the address string, we can parse it as we did before:
GetZipCode = Split(Trim$(Split(formattedAddress, Chr$(44))(2)), Chr$(32))(1)
End note:
You can use Postman, amongst other tools, to test API calls, and in this case inspect the JSON response. Indeed, to see what kind of response you are getting full stop.
Help:
It is very quick and easy to set up a project, generate an API key and get started. Maybe 10 minutes to read through and perform.
Instructions on setting up a project and getting an API key
Enabling the API
Understanding how to make API calls to the Geocoding API

How to parameterise a object member in VB.NET for WSDL

I have some code that populates a SOAP request in VB.NET. I get the data from a SQL query and run through the objects members to populate each one. I'm trying to find a quick(ish) way to not provide the member when it is empty rather than checking each value before applying it. As the SQL data comes in from a pipe delimited file in the first place we never get NULL just empty cells which get sent in the SOAP request as "".
Is there a way to define an objects member using a variable rather than its literal name?
UpLB_request_Items.Spare8 = Convert.ToString(SourceDataSet.tables(0).Rows(i)(colSPARE8))
UpLB_request_Items.Spare9 = Convert.ToString(SourceDataSet.tables(0).Rows(i)(colSPARE9))
UpLB_request_Items.Spare10 = Convert.ToString(SourceDataSet.tables(0).Rows(i)(colSPARE10))
Call IterateObject(UpLB_request_Items)
So this populates each member with the value from the SQL data (SourceDataSet), then I could send the completed object down to a Sub to check each value before I actually send it to the webservice
Sub IterateObject(objName)
Dim CollName = ""
For Each m As System.Reflection.PropertyInfo In objName.GetType().GetProperties()
If m.CanRead Then
If m.PropertyType.Name = "String" Then
CollName = m.Name
Dim CollVal = CallByName(objName, CollName, CallType.Get)
If CollVal = "" Then
objName.CollName = Nothing 'this is the tricky bit
End If
End If
End If
Next
End Sub
The bit where it obviously breaks is objName.CollName = Nothing as it will then say that the object doesn't have a member called CollName and I'm not sure if there is a way to force the code to evaluate CollName to its value (e.g. SPARE8 rather than the string "CollName")
I went with this in the end
UpLB_request_Items.Spare4 = Strings.Replace(Convert.ToString(SourceDataSet.Tables(0).Rows(i)(colSPARE4)), "", Nothing)

VB Script code to read column value from lotus notes database

Set domSession = CreateObject("Notes.NotesSession")
Dim domDatabase
Set domDatabase = domSession.GetDatabase("server Name", "xyz.nsf")
Set domView = domDatabase.GetView("All Projects")
Up to here it's ok. But I try lot but unable to read the document values.
I am looking for the vbscript code to read the document data.
Add this:
Dim i%
Dim s$
Dim doc As Object
For i% = 1 to domView.EntryCount
Set doc = domView.GetNthDocument(i%)
s$ = doc.GetItemValue("itemname")(0)
' Do whatever you want with the string value which is now stored in s$
Next
This code assumes the items are text type. You could use simply Dim s to get any item type (number, date).
To get column values from the view you can use doc.ColumnValues(2). This returns the value of 3rd column (0 is first).