I have a multidimensional JSON array returned via a web request (http://www.coincap.io/history/365day/BTC). I want to cycle through the 2nd entry and retrieve its nested values.
If this was a normal array, I'd use:
For Each item In response
logic, logic, logic
currentRow = currentRow + 1
Next
This web request returns a JSON-response with 3 entries: market_cap, price, and volume. I just want to cycle through response(1) and get the price values. Each entry in price contains two keys, 0 and 1.
I would imagine that I could accomplish this by doing
For Each item in response(1)
Cells(currentRow, 1).Value = item(0)
Cells(currentRow, 2).Value = item(1)
currentRow = currentRow + 1
Next
I've also considered For Each item in response("price"). Neither works.
Sub Tester()
Dim json As String
Dim sc As Object
Dim o, n, i, p
Set sc = CreateObject("scriptcontrol")
sc.Language = "JScript"
json = HttpGet("http://www.coincap.io/history/365day/BTC")
sc.Eval "var obj=(" & json & ")" 'evaluate the json response
'add a couple of accessor functions
sc.AddCode "function numPrices(){return obj.price.length;}"
sc.AddCode "function getPrice(i){return obj.price[i];}"
n = sc.Run("numPrices")
For i = 0 To n - 1
p = Split(sc.Run("getPrice", i), ",")
Debug.Print i, p(0), p(1)
Next i
End Sub
Function HttpGet(url As String) As String
Dim oHTML As Object
Set oHTML = CreateObject("Microsoft.XMLHTTP")
With oHTML
.Open "GET", url, False
.send
HttpGet = .responsetext
End With
End Function
Related
I'm new to StackOverflow, and I'm struggling with displaying data from a dimensional JSON array in VBA (Excel). Could you please help?
Below are the codes I'm using for displaying the data of "ShippingMethod" from the JSON.
Example of JSON:
As you are able to see the "Data" is the first object.
"Shipments" and "DisplayItems" are the array of "Data".
Also, there are multiple objects inside the "Shipments" array.
For example "ShippingMethod" and "ShippingName", and my goal is to display the data of these objects. ("LC") and ("No Charge - Lowest Cost 3-5 Day Delivery")
Here are my codes:
First method:
Dim Json1 As Dictionary
Set Json1 = JsonConverter.ParseJson(strResponse3)
home.Activate
home.Cells(x, 2) = Json1("Data")("Shipments")(1)("ShippingMethod")
Second method:
Dim Json1 As Dictionary
Set Json1 = JsonConverter.ParseJson(strResponse3)
home.Activate
x = 42
For Each Item In Json1("Data")("Shipments")
home.Cells(x, 2) = Item("ShippingMethod")
x = x + 1
Next
I'm not able to display the data of these "ShippingMethod" and "ShippingName" objects as I'm getting an error message "Run-time error '13': Type mismatch" from the VBA.
NOTE: I'm using the same method on another JSON XML, it's able to retrieve the data from the dimensional JSON array without any issues.
Update on my codes(7/8)
Dim Json1 As Dictionary, x As Long, y As Long
Dim shipments
home.Activate
x = 42
Set Json1 = JsonConverter.ParseJson(strResponse3)
Set shipments = Json1("Data")("Shipments") '<-- Getting error "Run-time error '13': Type mismatch"'
For y = 3 To shipments.count
home.Cells(x, 2) = shipments(y)("ShippingMethod")
x = x + 1
Next
Error message_screenshot for Set shipments = Json1("Data")("Shipments")
The VBA highlight this line of code after selecting "Debug" button.
Note: try the below debug.print. However, the data returned as "0".
Debug.Print VarType(Json1("Data")("Shipments"))
Update on my codes(7/21)
Dim Json1 As Dictionary, x As Long, y As Long
Dim FSO, ts, s As String
Dim shipments
home.Activate
' write json to file
Set FSO = CreateObject("Scripting.FileSystemObject")
s = ThisWorkbook.Path & "\strResponse3.json"
Set ts = FSO.CreateTextFile(s)
ts.Write strResponse3
ts.Close
MsgBox Len(strResponse3) & " bytes written to " & s
x = 42
Set Json1 = JsonConverter.ParseJson(strResponse3)
Debug.Print "Json1", VarType(Json1)
Debug.Print "Json1(Data)", VarType(Json1("Data"))
Debug.Print "Json1(Data)(PriceSummary)", VarType(Json1("Data")("PriceSummary"))
Debug.Print "Json1(Data)(Shipments)", VarType(Json1("Data")("Shipments"))
Debug.Print "Json1(Data)(DisplayItems)", VarType(Json1("Data")("DisplayItems"))
home.Cells(x, 1) = Json1("Data")("Orders")("ShipmentId")
Set shipments = Json1("Data")("Shipments")
'home.Activate
For i = 1 To shipments.count
Cells(x, 2) = shipments(i)("ShippingMethod")
x = x + 1
Next
In the Txt output file, I noticed it only returning the data of "Data":{"PriceSummary":{,. Please see the beginning of the data TXT OUTPUT screenshot and compare it with the JSON screenshot.
Also, please check the ending of the data TXT OUTPUTand compare it with the JSON screenshot. The data only contain whenever inside the "PurchaseSummary" and there is no data with the arrays "Shipment" and "DisplayItems".
Here's the Debug.Print screenshot. No data returns for Shipments and DisplayItems.
I strongly believe there is something wrong with the JSON. (Kindly please refer back to the very first screenshot for the JSON pattern)
Debug.Print result:
Json1 9
Json1(Data) 9
Json1(Data)(PriceSummary) 9
Json1(Data)(Shipments) 0
Json1(Data)(DisplayItems) 0
Update - added JSON file export and test data.
Option Explicit
Sub test()
Dim Json1 As Dictionary, x As Long, i As Long
Dim shipments
Dim FSO, ts, strResponse3 As String, s As String
strResponse3 = "{""Data"" : {" & _
"""Shipments"":[" & _
"{""ShippingMethod"":""LC""}," & _
"{""ShippingMethod"":""LC""}," & _
"{""ShippingMethod"":""LC""}" & _
"]}}"
' write json to file
Set FSO = CreateObject("Scripting.FileSystemObject")
s = ThisWorkbook.Path & "\strResponse3.json"
Set ts = FSO.CreateTextFile(s)
ts.Write strResponse3
ts.Close
MsgBox Len(strResponse3) & " bytes written to " & s
Set Json1 = JsonConverter.ParseJson(strResponse3)
Debug.Print "Json1", VarType(Json1)
Debug.Print "Json1(Data)", VarType(Json1("Data"))
Debug.Print "Json1(Data)(Shipments)", VarType(Json1("Data")("Shipments"))
Set shipments = Json1("Data")("Shipments")
'home.Activate
x = 42
For i = 1 To shipments.Count
Cells(x, 2) = shipments(i)("ShippingMethod")
x = x + 1
Next
End Sub
I receive run-time error '13' Type mismatch when trying to import some data via IEX API using JSON.
I receive the error when setting the values for the cells in the For Each loop.
Here's a link to view the API data:
https://api.iextrading.com/1.0/stock/aapl/financials?period=annual
Sub getFinancials()
'Write to ws
Dim ws As Worksheet
Set ws = Sheets("Financials")
Dim ticker As String
ticker = ws.Range("P7").value
Dim lastrow As Long
lastrow = ws.Cells(Rows.Count, "A").End(xlUp).Row
'Clear Range
ws.Range("A1:L" & lastrow).Clear
'Array Column Headers
Dim myarray As Variant
myarray = Array("reportDate", "grossProfit", "costOfRevenue", "operatingRevenue", "totalRevenue", "operatingIncome", "netIncome", "researchAndDevelopment", "operatingExpense", "currentAssets", "totalAssets", "totalLiabilities", "currentCash", "currentDebt", "totalCash", "totalDebt", "shareholderEquity", "cashChange", "cashFlow", "operatingGainsLosses")
Arrsize = UBound(myarray) - LBound(myarray) + 1
Dim rngTarget As Range
Set rngTarget = ws.Range(Cells(2, 1), Cells(Arrsize + 1, 1))
rngTarget.value = Application.Transpose(myarray)
'Send web request for API Data
u = "https://api.iextrading.com/1.0/stock/" & ticker & "/financialsperiod=annual"
' https://api.iextrading.com/1.0/stock/aapl/financials?period=annual
Set myrequest = CreateObject("WinHttp.WinHttpRequest.5.1")
myrequest.Open "Get", u
myrequest.Send
'Parse JSON
Dim JSON As Object
Set JSON = JsonConverter.ParseJson(myrequest.ResponseText)
'Get # of Objects in Array
Dim arrayLen As Integer
arrayLen = JSON.Count
'Loop through Elements
Dim element As Variant
Dim x, y, r As Integer
r = 2
y = 2
x = 1
While x < arrayLen + 1
For Each element In myarray
ws.Cells(r, y).value = JSON(2)(element)
y = y + 1
Next element
y = 2
x = x + 1
r = r + 1
Wend
End Sub
I just ran the JSON through the converter and this is the structure that I get:
-Dictionary(2 items)
--Collection(4 items)
---Dictionary(20 items)
You need to extract the data accordingly. Collections can be looped through with a simple for each loop. Dictionarys can be looped through with the following structure.
Option Explicit
Sub PrintFinancialReports()
Dim apiURL As String
apiURL = "https://api.iextrading.com/1.0/stock/aapl/financials?period=annual"
Dim myrequest As WinHttpRequest
Set myrequest = New WinHttpRequest
myrequest.Open "Get", apiURL
myrequest.Send
Debug.Print myrequest.ResponseText ' print received JSON to check if it is valid
Dim FinancialReportQuery As Dictionary
Set FinancialReportQuery = JsonConverter.ParseJson(myrequest.ResponseText)
Debug.Print FinancialReportQuery.Item("symbol")
Dim Reports As Collection
Set Reports = FinancialReportQuery.Item("financials")
Dim report As Dictionary
For Each report In Reports
Dim reportContentKey As Variant '<-- variant is needed to loop a dictionary
For Each reportContentKey In report
Debug.Print reportContentKey, report.Item(reportContentKey)
Next reportContentKey
Next report
End Sub
Hope this helps
I am trying to access nested JSON values that come back from the API that I am working with at the moment. There seem to be no field names to use in this JSON, making it very difficult to follow most examples online.
API URL - CLICK HERE
I am using VBA-JSON through this process, and I've got it to successfully display "responseText" in MsgBox.
I am looking for a way to make this code work.
Public Sub exceljson()
Dim http As Object, JSON As Object, i As Integer
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "GET", "https://api.bitfinex.com/v2/candles/trade:5m:tEOSUSD/hist?start=1535760000000&end=1538265600000&sort=1", False
http.Send
Set JSON = ParseJson(http.responseText)
i = 2
For Each Item In JSON
Sheets(1).Cells(i, 1).Value = Item("one") ' Items reference as an example
Sheets(1).Cells(i, 2).Value = Item("two")
Sheets(1).Cells(i, 3).Value = Item("three")
Sheets(1).Cells(i, 4).Value = Item("four")
Sheets(1).Cells(i, 5).Value = Item("five")
i = i + 1
Next
MsgBox ("complete")
End Sub
In my answer to Using VBA and VBA-JSON to access JSON data from Wordpress API
, I wrote a function, PrintJSONAccessors(), which breaks down how to access the data in a JSON structure.
Checking the JSON object in the Locals Window reveals that it consists of a collection of collections.
Checking the TypeName of the item in the Immediate Window also reveals that item is indeed a collection'
?TypeName(Item)
Collection
PrintJSONAccessors JSON, "?JSON"
The code will output the correct way to access the data
Here is how you can access the items of the Collection
For Each Item In JSON
Sheets(1).Cells(i, 1).Value = Item(1) ' Items reference as an example
Sheets(1).Cells(i, 2).Value = Item(2)
Sheets(1).Cells(i, 3).Value = Item(3)
Sheets(1).Cells(i, 4).Value = Item(4)
Sheets(1).Cells(i, 5).Value = Item(5)
i = i + 1
Next
I would write a function to convert the JSON data into an Array
Public Sub exceljson()
Dim http As Object
Set http = CreateObject("MSXML2.XMLHTTP")
http.Open "GET", "https://api.bitfinex.com/v2/candles/trade:5m:tEOSUSD/hist?start=1535760000000&end=1538265600000&sort=1", False
http.Send
Dim results As Variant
results = BitfinexTextToArray(http.responseText)
Worksheets(1).Range("A1").Resize(UBound(results), UBound(results, 2)).Value = results
MsgBox ("complete")
End Sub
Function BitfinexTextToArray(responseText As String) As Variant
Dim item As Variant, JSON As Object
Dim MaxColumns As Long
Set JSON = ParseJson(responseText)
For Each item In JSON
If item.Count > MaxColumns Then MaxColumns = item.Count
Next
Dim results As Variant
ReDim results(1 To JSON.Count, 1 To MaxColumns)
Dim c As Long, r As Long
For Each item In JSON
r = r + 1
For c = 1 To item.Count
results(r, c) = item(c)
Next
Next
BitfinexTextToArray = results
End Function
I have been struggling with this for quite some time, but the error dialogue box that pops up isn't exactly the most helpful. I'm trying to extract a list of names from the worksheet and assigning them to an array using the range function. I tried and tried, but I couldn't seem to get it to work, so I tried reading in the cells 1 by 1 instead, using the Do Until Loop. I didn't expect to be posting this here, so the code of what I was doing before, is already gone, but here's an example:
Dim RangeList As Variant
RangeList = ThisWorkbook.Worksheets("Plan").Range("H1:H132").Value2
I switched it to the next method in hopes that it would lead to a more straightforward approach:
ReDim ResourceList(ResourceLength - 1)
I = 1
Do Until ThisWorkbook.Worksheets("Plan").Cells(I, 8).Value = ""
ResourceList(I) = ThisWorkbook.Worksheets("Plan").Cells(I, 8).Value
Workbooks("NEW PROJECT PLAN").Worksheets("Console").Cells(I, 2).Value = Resource
I = I + 1
Loop
The first one returns an empty range that 'Can't find any cells' and the second one gave me an array of empty strings 169 items long. I feel like I'm pounding my head against a brick wall on this one, any help would be appreciated.
Here is the entirety of the code that I'm trying to troubleshoot:
'Collects the List of Resources
Dim ResourceLength As Long, I As Integer
Dim ResourceList() As String
ResourceLength = ThisWorkbook.FinalRow(8, "Plan")
MsgBox ("Final Row is: " & ResourceLength) 'The Last row used in column 8
ReDim ResourceList(ResourceLength - 1)
I = 1
Do Until ThisWorkbook.Worksheets("Plan").Cells(I, 8).Value = ""
ResourceList(I - 1) = ThisWorkbook.Worksheets("Plan").Cells(I, 8).Value
Workbooks("NEW PROJECT PLAN").Worksheets("Console").Cells(I, 2).Value = Resource
I = I + 1
Loop
ResourceList = ThisWorkbook.FilterArray(ResourceList)
Dim myCount As Integer
Dim Source As Variant
For Each Source In ResourceList
Worksheets("Console").Cells(myCount, 1).Value = Source
myCount = myCount + 1
Next Source
Here is the FilterArray Function:
Public Function FilterArray(UnsortedArray As Variant) As Variant
Dim Intermediate() As Variant
Dim UItem As Variant
' Runs through each item and compares it to the list of items found, if it finds repeats, it throws them out.
For Each UItem In UnsortedArray
If Not ArrayItemExist(Intermediate, UItem) Then
' The Item does not Exist
ReDim Intermediate(UBound(Intermediate) + 1)
Intermediate(UBound(Intermediate)) = UItem
End If
Next UItem
' Returns the Sorted Array.
FilterArray = Intermediate
End Function
Private Function ArrayItemExist(TargetArray() As Variant, TargetItem As Variant) As Boolean
'Searches an Array for TargetItem and returns a boolean stating whether it exists within the Array or not.
Dim ItemFound As Boolean
Dim SItem As Variant
ItemFound = False
For Each SItem In TargetArray
If TargetItem = SItem Then
ItemFound = True
Exit For
End If
Next SItem
ArrayItemExist = ItemFound
End Function
Public Function FinalRow(Column As Integer, Sheet As String) As Long
' Finds the last Row used in the spreadsheet.
FinalRow = Worksheets(Sheet).Cells(Rows.Count, Column).End(xlUp).Row
End Function
When I try to run the software, I receive an error that the For Loop is not initialized, which I traced back to the 'ResourceList' Array/Range being empty.
[Edit]
This function is used to prep an array of names that are extracted from a list of dropdown box resources. This list may contain multiple instances of the same name, so it's sent to the FilterArray function to sort the array into an array with just one instance of each name. Example:
Before and after sorting
After this, it's sent to a module that will inject each name into a dictionary with a corresponding amount of hours that the person is scheduled to work.
I am looking for a loop function/syntax that will allow my loop to cease once the website I am pulling JSON arrays from has no additional arrays left to parse (variable / unknowable number of arrays).
Thank you for the insight.
sheetCount = 1
i = 1
urlArray = Array("URL array list")
Dim MyRequest As Object
Set MyRequest = CreateObject("WinHttp.WinHttpRequest.5.1")
Dim MyUrls
MyUrls = urlArray
Dim k As Long
Dim Json As Object
For k = LBound(MyUrls) To UBound(MyUrls)
With MyRequest
.Open "GET", MyUrls(k)
.Send
Set Json = JsonConverter.ParseJson(.ResponseText)
Do Until ''[NEED HELP HERE]
Sheets("Sheet" & sheetCount).Cells(i, 1) = Json("cars")(i)("carType")
Sheets("Sheet" & sheetCount).Cells(i, 2) = Json("cars")(i)("fare")("carprice")
i = i + 1
Loop
End With
sheetCount = sheetCount + 1
Next
You are missing the UBound function.
Other notes
No code without Option Explicit, period. No exceptions.
Make small functions that do one thing only.
Add references to the libraries you use instead of using CreateObject. It will make your life a lot easier because this way you get compile-time type checking and Intellisense.
It's safer to use the Exists() method to check if a dictionary key exists before you try to access it. Trying to access a non-existing key will throw a run-time error.
I'm silently assuming that you are using https://github.com/VBA-tools/VBA-JSON.
This should be close enough:
Option Explicit
Function GetJson(ByVal url As String) As Dictionary
With New WinHttpRequest ' see http://stackoverflow.com/a/3119794/18771
.Open "GET", url
.Send
Set GetJson = JsonConverter.ParseJson(.ResponseText)
End With
End Function
Sub FillCarInfo(data As Dictionary, sheet As Worksheet)
Dim i As Integer, car As Dictionary
For i = 0 To UBound(data("cars")) - 1
Set car = data("cars")(i)
' you probably should use If car.Exists("carType") Then
sheet.Cells(i, 1) = car("carType")
sheet.Cells(i, 1) = car("fare")("carprice")
Next i
End Sub
Sub FillMultipleCarInfo(urls As Variant, book As Workbook)
Dim i As Integer, data As Dictionary, sheet As Worksheet
For i = 0 To UBound(urls) - 1
Set data = GetJson(urls(i))
Set sheet = book.Sheets(i + 1)
FillCarInfo data, sheet
Next i
End Sub
Usage
Dim myUrls As Variant
myUrls = Array("URL array list")
FillMultipleCarInfo myUrls, ActiveWorkbook