I'm getting data from a AQI API from China, I'm putting some of the data into a SQL server database and that works just dandy. I also decided to add everything returned from the API into a JSON file so I can access it later.
Whenever I try to append the string returned by the API into the file I get a "Invalid procedure call or argument" error
This is what I have done so far:
Public Sub AddToJson(Jsonline As String)
Dim strfile As String
Dim fso As New FileSystemObject
Dim fsoStream As TextStream
Dim iexist As String
Dim stradd As String
strfile = "c:\JSON_AQI.json"
stradd = Replace(Jsonline, Chr(34), Chr(34) & Chr(34) & Chr(34) & Chr(34))
Debug.Print stradd
iexist = Dir(strfile)
'check if the file exists
If iexist = "" Then
'if it exists, open it and add the line
Set fsoStream = fso.CreateTextFile(strfile)
Else
'if it doesn't exist, create it and add the line
Set fsoStream = fso.OpenTextFile(strfile, ForAppending)
End If
fsoStream.WriteLine stradd
fsoStream.Close
Set fsoStream = Nothing
Set fso = Nothing
End Sub
This is what I pass as the Jsonline parameter:
{"status":"ok","data":{"aqi":164,"idx":7130,"attributions":[{"name":"Hunan Environmental Protection Agency (????????)"},{"name":"China National Urban air quality real-time publishing platform (??????????????)"}],"city":{"geo":[33.8561,115.7831],"name":"sanguó lanshèng gong, Bozhou"},"dominentpol":"pm25","iaqi":{"co":{"v":14.8},"no2":{"v":24.7},"o3":{"v":45.9},"pm10":{"v":97},"pm25":{"v":164},"so2":{"v":5.1}},"time":{"s":"2017-04-06 04:00:00","tz":"+08:00","v":1491451200}}}
as you can see I tried adding extra double quotes to the string to no avail, is there anything else that I'm missing?
Actually the received JSON contains Chinese characters instead of ? you posted and should look like:
{
"status": "ok",
"data": {
"aqi": 164,
"idx": 7130,
"attributions": [
{
"name": "Hunan Environmental Protection Agency (湖南省环境保护厅)"
},
{
"name": "China National Urban air quality real-time publishing platform (全国城市空气质量实时发布平台)"
}
],
"city": {
"geo": [
33.8561,
115.7831
],
"name": "sanguó lanshèng gong, Bozhou"
},
"dominentpol": "pm25",
"iaqi": {
"co": {
"v": 14.8
},
"no2": {
"v": 24.7
},
"o3": {
"v": 45.9
},
"pm10": {
"v": 97
},
"pm25": {
"v": 164
},
"so2": {
"v": 5.1
}
},
"time": {
"s": "2017-04-06 04:00:00",
"tz": "+08:00",
"v": 1491451200
}
}
}
Therefore you have to set encoding of the snippet being appended as Unicode explicitly. I suggest to use the below simple functions to read, write and append text files via Scripting.FileSystemObject:
Function ReadTextFile(sPath As String, lFormat As Long) As String
' lFormat -2 - System default, -1 - Unicode, 0 - ASCII
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 1, False, lFormat)
ReadTextFile = ""
If Not .AtEndOfStream Then ReadTextFile = .ReadAll
.Close
End With
End Function
Sub WriteTextFile(sContent As String, sPath As String, lFormat As Long)
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 2, True, lFormat)
.Write sContent
.Close
End With
End Sub
Sub AppendTextFile(sContent As String, sPath As String, lFormat As Long)
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 8, True, lFormat)
.Write sContent
.Close
End With
End Sub
So you can append the string by the calling:
AppendTextFile Jsonline & vbCrLf, "C:\JSON_AQI.json", -1
Related
I am wondering to create a json from VBA Outlook to export email as ticket on Osticket System
Everithing working well except when there's multiple attachments
I need to have this syntax
{
"alert": "true",
"autorespond": "true",
"source": "API",
"name": "Angy User",
"email": "Angry#somewhere.com",
"subject": "Help",
"topicId": "1",
"message": "data:text/html,</body></html>Please Help</body></html>",
"attachments": [
{ "MyFile.png": "data:image/png;base64,JVBERi0........." },
{ "MyFile.png": "data:image/png;base64,JVBERi0........." },
]
}
But using my code i get this
{
"alert": "true",
"autorespond": "true",
"source": "API",
"name": "Angy User",
"email": "Angry#somewhere.com",
"subject": "Help",
"topicId": "1",
"message": "data:text/html,</body></html>Please Help</body></html>",
"attachments": [
{ "MyFile.png": "data:image/png;base64,JVBERi0.........",
"MyFile.png": "data:image/png;base64,JVBERi0........." },
]
}
I use this to create the json
Dim Body As New Dictionary
Body.Add "alert", "true"
Body.Add "autorespond", "true"
Body.Add "source", "API"
Body.Add "name", myMsg.SenderName
Body.Add "email", FromAddress
Body.Add "subject", myMsg.Subject
Body.Add "topicId", CStr(rubriq)
Body.Add "message", "data:text/html," & strData 'myMsg.HTMLBody
Body.Add "attachments", Array(Attm1) 'attachments
Dim json As String
json = JsonConverter.ConvertToJson(Body, Whitespace:=" ")
Where the Attm1 is a dictionary filled in FOR loop
Attm1.Add oFile.FileName, "data:" & _
oFile.PropertyAccessor.GetProperty(PR_ATTACH_CONTENT_TYPE) & _
";" & "base64," & n.nodeTypedValue
I used this function
https://github.com/VBA-tools/VBA-JSON
The loop code
Dim attachments As New Collection
If myMsg.attachments.Count > 0 Then
Set fs = CreateObject("Scripting.FileSystemObject")
Set nAtt = xmlTicket.createElement("attachments")
nodeTicket.appendChild nAtt
For i = 1 To myMsg.attachments.Count
Set oFile = myMsg.attachments.Item(i)
'I only add attachments up to a limit in size
If oFile.Size <= MAX_ATTACHMENT Then
sTmpFile = fs.GetTempName
oFile.SaveAsFile sTmpFile
'Attachment data is always base64-coded
n.dataType = "bin.base64"
'The ADODB.Stream tweak allows to read binary files
Set data = CreateObject("ADODB.Stream")
data.Type = 1 'Binary
data.Open
data.LoadFromFile sTmpFile
'MSXML will base64-code it for us
n.nodeTypedValue = data.Read
'Using the bin.base64 structure means adding namespace'd attributes.
'For some reason, osTicket will complain for each extra attribute, so
'we get to clean up
n.Attributes.removeNamedItem "dt:dt"
'For some reason, getting the content-type is very unclear in Outlook
Set a = xmlTicket.createAttribute("type")
a.Value = oFile.PropertyAccessor.GetProperty(PR_ATTACH_CONTENT_TYPE)
n.Attributes.setNamedItem a
Dim Attm1 As New Dictionary
Attm1.Add oFile.FileName, "data:" & oFile.PropertyAccessor.GetProperty(PR_ATTACH_CONTENT_TYPE) & ";" & "base64," & n.nodeTypedValue
Kill sTmpFile
End If
Next
End If
Try something like this. It's easier to manage if you split out the various jobs into separate methods.
Const MAX_ATTACHMENT As Long = 500000 'or whatever
Sub MainSub()
Dim Body As Object, dict As Object, i As Long, json As String
Dim myMsg As Outlook.MailItem
'...
'...
Body.Add "attachments", New Collection
If myMsg.attachments.Count > 0 Then
For i = 1 To myMsg.attachments.Count
Set dict = AttachmentDict(myMsg.attachments.Item(i))
If Not dict Is Nothing Then 'check conversion happened
Body("attachments").Add dict
End If
Next
End If
json = JsonConverter.ConvertToJson(Body, Whitespace:=" ")
'...
'...
End Sub
'create a dictionary from an attachment if it meets the size limit
Function AttachmentDict(att As Outlook.Attachment)
Dim dict As Object, fso As Object, sTmpFile As String
If att.Size < MAX_ATTACHMENT Then
Set dict = CreateObject("Scripting.Dictionary")
Set fso = CreateObject("Scripting.FileSystemObject")
sTmpFile = fso.GetSpecialFolder(2) & "\" & fso.GetTempName
att.SaveAsFile sTmpFile
dict.Add att.Filename, "data:" & _
att.PropertyAccessor.GetProperty(PR_ATTACH_CONTENT_TYPE) & _
";" & "base64," & FileToBase64(sTmpFile)
Set AttachmentDict = dict
End If
End Function
Function FileToBase64(FilePath As String) As String
Const adTypeBinary = 1 ' Binary file is encoded
Dim objXML, objDocElem, objStream
' Open data stream from file
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = adTypeBinary
objStream.Open
objStream.LoadFromFile FilePath
Set objXML = CreateObject("MSXml2.DOMDocument")
Set objDocElem = objXML.createElement("Base64Data")
objDocElem.DataType = "bin.base64"
objDocElem.nodeTypedValue = objStream.Read()
FileToBase64 = objDocElem.Text
End Function
I am trying to get "image_id" from the below json in different columns of a row.
[
{
"spin": "HM4C6L",
"attributes": {
"product name": "Everest Kutilal Coarse Ground Red Chilli Powder ",
},
"bar_code": {
"valid": true,
"id": "89017817",
"type": "UPC"
},
"spin_state": "LIVE",
"meta": {
"updated-by": "undefined"
},
"version": null,
"images": [
{
"image_id": "dvuewrnauxdroqcapjiu",
"image_name": "masala and spice_HM4A7I2C6L_MN.JPG",
"shot_type": "MN"
},
{
"image_id": "tcku7lwarkv8ch0ao9cu",
"image_name": "masala and spice_HM4A7I2C6L_AL1.JPG",
"shot_type": "AL1"
},
{
"image_id": "b2znlmm59plprrkmkujs",
"image_name": "masala and spice_HM4A7I2C6L_AL2.jpg",
"shot_type": "AL2"
}
]
}
]
I tried Cannot iterate when parsing HTML table using JSON-VBA and Loop through the JSON object keys in excel vba.
Sub getimage()
Dim current As Workbook
Dim sht As Worksheet
Dim a, b, strUrl As String
Dim count As Variant
Set current = ActiveWorkbook
For Each sht In current.Worksheets
On Error Resume Next
'Application.ScreenUpdating = False
Set HTTPReq = CreateObject("WinHttp.WinHttpRequest.5.1")
count = Range("A1", Range("A1").End(xlDown)).Rows.count
For i = 2 To count
a = CStr(Range("A" & i).Value)
HTTPReq.Open "GET", "link" & a, False
HTTPReq.send
'Debug.Print HTTPReq.ResponseText
Dim Json, item As Object
Set Json = JsonConverter.ParseJson(HTTPReq.ResponseText)
For Each item In Json
Debug.Print item("images")
sht.Cells(i, B) = item("image_id")("1")
sht.Cells(i, B) = item("image_id")("2")
next Item
Next i
'Application.ScreenUpdating = True
End If
Next sht
End Sub
I need "dvuewrnauxdroqcapjiu" in cell B2, tcku7lwarkv8ch0ao9cu in cell C2, and "b2znlmm59plprrkmkujs" in cell C2, but my code is giving no output, no error.
A number of things.
Your json is malformed. There is an extra "," here:
"Everest Kutilal Coarse Ground Red Chilli Powder ",
which means jsonconverter will throw an error. The "," at the end would be to separate the current key-value pair from the next. There isn't a following pair so it should be removed.
Your path for accessing is wrong.
Here I am reading the corrected json from cell A1
Option Explicit
Public Sub test()
Dim json As Object, i As Long, item As Object, c As Long
i = 2: c = 2
Set json = JsonConverter.ParseJson([A1])(1)("images")
For Each item In json
ActiveSheet.Cells(i, c) = item("image_id")
c = c + 1
Next
End Sub
Cells(2,B) will expect B to be a variable as string literals are wrapped in "" i.e. "B". Also, you need a counter variable which is incremented or you will keep writing to the same cell.
good people of StackOverflow!
I am trying to figure out how to connect to Airtable API, get JSON from there and populate Access table with the results.
So far, at least I managed to restrict the request to give me only a few fields I need.The result looks like this:
{
"records": [{
"id": "rec008lgyvVmwk1F4",
"fields": {
"Date": "2018-02-28"
},
"createdTime": "2018-01-26T15:36:23.000Z"
}, {
"id": "rec02WozJeaGvfBfj",
"fields": {
"Hours": 1.5,
"Date": "2018-02-09",
"Project": ["Nonbillable"]
},
"createdTime": "2018-02-12T17:03:18.000Z"
}, {
"id": "rec05VxP0CYTsDYOA",
"fields": {
"Date": "2018-02-08"
},
"createdTime": "2018-02-01T10:29:52.000Z"
}, {
"id": "rec05xoQEm5iWIYmz",
"fields": {
"Hours": 0.75,
"Date": "2018-02-16",
"Project": ["2018 - Japan DLAs"]
},
"createdTime": "2018-02-19T09:29:18.000Z"
}]
}
From that point on I have read as many examples as I could find how to use VBA-JSON by Tim Hall (thank you Tim, for creating it :)
As far as I understand, in my case the ParseJson function returns a dictionary. Inside that is a collection named 'results' and inside that collection is another dictionary named 'fields'.
What I need are values for keys 'Hours', 'Date' and 'Project' from that dictionary.
I have tried to do those three loops (loop through dictionary inside collection inside dictionary) and was failing miserably many times with a variety of errors. Finally, I have come to the point where I see no more errors, the sub happily gives me "Import done!" message. Alas, my table is empty!
What, what am I doing wrong?
I hope I gave you enough information and thank you very much in advance for your help!
(If it matters, I'm working with 32-bit Access 2016 on 64-bit Windows)
Public Sub ImportJSON()
Dim reader As New XMLHTTP60
Dim JsonRetrieved As String
Dim Parsed As Scripting.Dictionary
Dim records As New Collection
Dim fields As Scripting.Dictionary
Dim item As Variant
Dim rs As New ADODB.Recordset
reader.Open "GET", "https://api.airtable.com/v0/apppLTTgKBsw5QmUX/myTable?fields[]=Project&fields[]=Hours&NOT({Hours} = '')&fields[]=Date&NOT({Date} = '')&maxRecords=4&api_key=mykey", False
reader.setRequestHeader "Accept", "application/json"
reader.Send
Do Until reader.ReadyState = 4
DoEvents
Loop
If reader.Status = 200 Then
rs.Open "tblAirtableImport", CurrentProject.Connection, _
adOpenKeyset, adLockOptimistic
JsonRetrieved = reader.responseText
'Debug.Print JsonRetrieved
Set Parsed = JsonConverter.ParseJson(JsonRetrieved)
'loop through dictionary 'Parsed'
Dim i As Long
For i = 0 To Parsed.Count - 1
'loop through collection 'records'. If we have hours logged into Airtable add new record (Hours, Date, Project) to Access table
For Each fields In records
'loop through dictionary 'fields'
Dim j As Long
For j = 0 To fields.Count - 1
If fields.Exists("Hours") Then
'MsgBox "We have hours in this row"
rs.AddNew
rs!AirtableHours = fields.item("Hours")
rs!AirtableDate = fields.item("Date")
rs!AirtableProject = fields.item("Project")
rs.Update
Else
MsgBox "No logged time."
End If
Next j
Next
Next i
MsgBox "Import done!"
Set Parsed = Nothing
Else
MsgBox "Ups, unable to import data. Reader status is: " & reader.Status
End If
End Sub
The entry Project holds an Array so take the first Item:
Set Parsed = JsonConverter.ParseJson(JsonRetrieved)
For Each record In Parsed("records")
Set fields = record("fields")
If fields.Exists("Hours") Then
rs.AddNew
rs!AirtableHours = fields("Hours")
rs!AirtableDate = fields("Date")
rs!AirtableProject = fields("Project")(1)
rs.Update
End If
Next
Thank you to #QHarr for working on this with me!
My goal is to grab the values for each of the nested categories from "orders"
my json:
{
"total": 14,
"_links": {
"next": {
"href": "/api/my/orders/selling/all?page=2&per_page=1"
}
},
"orders": [
{
"amount_product": {
"amount": "0.01",
"currency": "USD",
"symbol": "$"
},
"amount_product_subtotal": {
"amount": "0.01",
"currency": "USD",
"symbol": "$"
},
"shipping": {
"amount": "0.00",
"currency": "USD",
"symbol": "$"
},
"amount_tax": {
"amount": "0.00",
"currency": "USD",
"symbol": "$"
},
"total": {
"amount": "0.01",
"currency": "USD",
"symbol": "$"
},
"buyer_name": "Some Buyer",
"created_at": "2015-02-03T04:38:03-06:00",
"order_number": "434114",
"needs_feedback_for_buyer": false,
"needs_feedback_for_seller": false,
"order_type": "instant",
"paid_at": "2015-02-03T04:38:04-06:00",
"quantity": 1,
"shipping_address": {
"name": "Some Buyer",
"street_address": "1234 Main St",
"extended_address": "",
"locality": "Chicagoj",
"region": "IL",
"postal_code": "60076",
"country_code": "US",
"phone": "1231231234"
},
"local_pickup": false,
"shop_name": "Some Seller",
"status": "refunded",
"title": "DOD Stereo Chorus Extreme X GFX64",
"updated_at": "2015-03-06T11:59:27-06:00",
"payment_method": "direct_checkout",
"_links": {
"photo": {
"href": "https://reverb-res.cloudinary.com/image/upload/a_exif,c_thumb,f_auto,fl_progressive,g_south,h_192,w_192/v1386717988/h1zpe0sii9my0xm55htd.jpg"
},
"feedback_for_buyer": {
"href": "/api/orders/434114/feedback/buyer"
},
"feedback_for_seller": {
"href": "/api/orders/434114/feedback/seller"
},
"listing": {
"href": "/api/listings/47096"
},
"start_conversation": {
"href": "/api/my/conversations?listing_id=47096&recipient_id=302456"
},
"self": {
"href": "/api/my/orders/selling/434114"
},
"mark_picked_up": {
"href": "/api/my/orders/selling/434114/mark_picked_up"
},
"ship": {
"href": "/api/my/orders/selling/434114/ship"
},
"contact_buyer": {
"web": {
"href": "https://reverb.com/my/messages/new?item=47096-dod-stereo-chorus-extreme-x-gfx64&to=302456-yan-p-5"
}
}
},
"photos": [
{
"_links": {
"large_crop": {
"href": "https://reverb-res.cloudinary.com/image/upload/a_exif,c_thumb,f_auto,fl_progressive,g_south,h_640,q_85,w_640/v1386717988/h1zpe0sii9my0xm55htd.jpg"
},
"small_crop": {
"href": "https://reverb-res.cloudinary.com/image/upload/a_exif,c_thumb,f_auto,fl_progressive,g_south,h_296,q_85,w_296/v1386717988/h1zpe0sii9my0xm55htd.jpg"
},
"full": {
"href": "https://reverb-res.cloudinary.com/image/upload/a_exif,c_limit,f_auto,fl_progressive,h_1136,q_75,w_640/v1386717988/h1zpe0sii9my0xm55htd.jpg"
},
"thumbnail": {
"href": "https://reverb-res.cloudinary.com/image/upload/a_exif,c_thumb,f_auto,fl_progressive,g_south,h_192,w_192/v1386717988/h1zpe0sii9my0xm55htd.jpg"
}
}
}
],
"sku": "rev-47096",
"selling_fee": {
"amount": "0.00",
"currency": "USD",
"symbol": "$"
},
"direct_checkout_payout": {
"amount": "-0.24",
"currency": "USD",
"symbol": "$"
}
}
]
}
If I have one good example of how to work with the nested data I am sure I can get this to work. This is my current code, it doesn't work... this is the error- "the object doesn't support this property or method" on this line: For Each Amount_Product In Orders("amount_product"). What I am expecting is to be able to extract the value of each of the amount_product "items" and push them into variables so that I can then push them into a table.
Dim Json As Object
Dim FSO As New FileSystemObject
Dim JsonTS As TextStream
Dim JsonText As String
Dim Parsed As Dictionary
'set up variables to receive the values
Dim sAmount As String
Dim sCurrency As String
Dim sSymbol As String
'Read .json file
Set JsonTS = FSO.OpenTextFile("somefilepath.txt", ForReading)
JsonText = JsonTS.ReadAll
JsonTS.Close
'came from https://github.com/VBA-tools/VBA-JSON
Set Parsed = JsonConverter.ParseJson(JsonText)
Dim Values As Variant
Dim Orders As Dictionary
Dim NestedValue As Dictionary
Dim i As Long
i = 0
For Each Orders In Parsed("orders")
For Each NestedValue In Orders("amount_product")
sAmount = (Values(i, 0) = NestedValue("amount"))
sCurrency = (Values(i, 1) = NestedValue("currency"))
sSymbol = (Values(i, 2) = NestedValue("symbol"))
i = i + 1
Next NestedValue
Next Orders
I also tried this- based on some examples of code I have found, this doesn't work either:
For Each NestedValue In Parsed("orders")(1)("amount_product")
sAmount = (Values(i, 0) = NestedValue("amount"))
sCurrency = (Values(i, 1) = NestedValue("currency"))
sSymbol = (Values(i, 2) = NestedValue("symbol"))
i = i + 1
Next NestedValue
I tried using this VBA Parse Nested JSON example by #TimWilliams but was not successful in tweaking it to work with my Json. Same error, "object doesn't support this property or method" on the line "For Each NestedValue In Parsed("orders")(1)("amount_product")"
Ok solved (Oops....I think!). So, here are two versions dealing with the same JSON.
Version 1: A simple example showing you how to get the Amount_Product values you were after. Not the easiest to read syntax, but I have given the lengthy descriptions/syntax in version 2.
Version 2: Extracting all the values from the JSON.
Additional set-up requirements:
1) Reference required to MS Scripting Runtime in VBE > Tools > References
2) JSON Converter module by Tim Hall
Process:
I used TypeName(object) , at each stage, to understand which objects were being returned from the JSON. I have left some of these in (commented out as Debug.Print statements) so you have an idea what is going on at each stage.
Observations:
1) JsonConverter.ParseJson(JsonText) returns a dictionary to Parsed.
2) Parsed("orders") returns a collection which holds a single dictionary i.e. initialCollection(1)
3) That dictionary holds a variety of objects which is perhaps what is rather confusing.
If you run the following, to look at the objects in the dictionary:
Debug.Print TypeName(initialDict(key))
You discover what a busy little dictionary it is. It hosts the following:
Boolean * 3
Collection * 1
Dictionary * 9
Double * 1
String * 11
And so of course you keep delving into deeper levels of the nesting via these structures. The different handling, according to datatype, I have done via Select Case. I have tried to keep the terminology fairly straight forward.
How to use an Online JSON parser to examine structure:
So there are a number of online JSON parsers out there.
You pop your code in the left window (of the example I have given) and the right window shows the evaluation:
If you look at the initial red "[" ; this is the collection object you are getting with Parsed("orders").
Then you can see the first "{" before the "amount_product" which is your first dictionary within the collection.
And within that, associated with "amount_product" id, is the next dictionary where you see the next "{"
So you know you have to get the collection and then potentially iterate over two dictionaries to get the first set of values you were interested in.
I used a shortcut with Parsed("orders")(1)("amount_product").Keys ,in the first code example, to get to this inner dictionary to iterate over.
Results:
Code:
Version 1 (Simple):
Option Explicit
Public Sub test1()
Dim Json As Object
Dim FSO As New FileSystemObject
Dim JsonTS As TextStream
Dim JsonText As String
Set JsonTS = FSO.OpenTextFile("C:\Users\User\Desktop\Document.txt", ForReading)
JsonText = JsonTS.ReadAll
JsonTS.Close
Dim Parsed As Dictionary 'or As Object if not including reference to scripting runtime reference in library
Set Parsed = JsonConverter.ParseJson(JsonText)
Dim key As Variant
Dim sAmount As String 'Assume you will keep these as strings?
Dim sCurrency As String
Dim sSymbol As String
For Each key In Parsed("orders")(1)("amount_product").Keys
Dim currentString As String
currentString = Parsed("orders")(1)("amount_product")(key)
Select Case key
Case "amount"
sAmount = currentString
Case "currency"
sCurrency = currentString
Case "symbol"
sSymbol = currentString
End Select
Debug.Print key & ": " & currentString
Next key
End Sub
Version 2: Grab everything. More descriptive.
Option Explicit
Sub test2()
Dim Json As Object
Dim FSO As New FileSystemObject
Dim JsonTS As TextStream
Dim JsonText As String
Set JsonTS = FSO.OpenTextFile("C:\Users\User\Desktop\Document.txt", ForReading) 'change as appropriate
JsonText = JsonTS.ReadAll
JsonTS.Close
Dim Parsed As Dictionary
Set Parsed = JsonConverter.ParseJson(JsonText)
Dim initialCollection As Collection
Set initialCollection = Parsed("orders")
' Debug.Print initialCollection.Count ' 1 item which is a dictionary
Dim initialDict As Dictionary
Set initialDict = initialCollection(1)
Dim key As Variant
Dim dataStructure As String
For Each key In initialDict.Keys
dataStructure = TypeName(initialDict(key))
Select Case dataStructure
Case "Dictionary"
Dim Key1 As Variant
For Each Key1 In initialDict(key).Keys
Select Case TypeName(initialDict(key)(Key1))
Case "String"
Debug.Print key & " " & Key1 & " " & initialDict(key)(Key1) 'amount/currency/symbol
Case "Dictionary"
Dim Key2 As Variant
For Each Key2 In initialDict(key)(Key1).Keys
'Debug.Print TypeName(initialDict(key)(Key1)(Key2)) 'strings and one dict
Select Case TypeName(initialDict(key)(Key1)(Key2))
Case "String"
Debug.Print key & " " & Key1 & " " & Key2 & " " & initialDict(key)(Key1)(Key2)
Case "Dictionary"
Dim Key3 As Variant
For Each Key3 In initialDict(key)(Key1)(Key2).Keys
'Debug.Print TypeName(initialDict(key)(Key1)(Key2)(Key3)) 'string only
Debug.Print initialDict(key)(Key1)(Key2)(Key3)
Next Key3
End Select
Next Key2
Case Else
MsgBox "Oops I missed this one"
End Select
Next Key1
Case "String", "Boolean", "Double"
Debug.Print key & " : " & initialDict(key)
Case "Collection"
'Debug.Print TypeName(initialDict(key)(1)) 'returns 1 Dict
Dim Key4 As Variant
For Each Key4 In initialDict(key)(1).Keys 'Debug.Print TypeName(initialDict(key)(1)(Key4)) 'returns a dictionary
Dim Key5 As Variant
For Each Key5 In initialDict(key)(1)(Key4).Keys ' Debug.Print TypeName(initialDict(key)(1)(Key4)(Key5)) returns 4 dictionaries
Dim Key6 As Variant
For Each Key6 In initialDict(key)(1)(Key4)(Key5).Keys 'returns string
Debug.Print key & " " & Key4 & " " & Key5 & " " & Key6 & " " & initialDict(key)(1)(Key4)(Key5)(Key6)
Next Key6
Next Key5
Next Key4
Case Else
MsgBox "Oops I missed this one!"
End Select
Next key
End Sub
Final observation:
To be consistent, and to aid demonstrating what is going on, I have added all the .Keys, but it is unnecessary, when iterating in a For Each Loop over a Dictionary, to put .Keys, as shown in test below and in the embedded gif:
Option Explicit
Private Sub test()
Dim testDict As Dictionary
Set testDict = New Dictionary
testDict.Add "A", 1
testDict.Add "B", 2
Dim key As Variant
For Each key In testDict
Debug.Print key & ":" & testDict(key)
Next key
End Sub
So for example:
For Each key In initialDict.Keys => For Each key In initialDict
I combined V1 and V2 above to produce the results, which was to capture values and save them into variables. This is my edited code: (I am still working on creating all of the cases and variables)
Dim Json As Object
Dim FSO As New FileSystemObject
Dim JsonTS As TextStream
Dim JsonText As String
Set JsonTS = FSO.OpenTextFile("C:\some.txt", ForReading) 'change as appropriate
JsonText = JsonTS.ReadAll
JsonTS.Close
Dim Parsed As Dictionary
Set Parsed = JsonConverter.ParseJson(JsonText)
Dim initialCollection As Collection
Set initialCollection = Parsed("orders")
Debug.Print initialCollection.Count ' 1 item which is a dictionary
Dim initialDict As Dictionary
Set initialDict = initialCollection(1)
Dim Key As Variant
Dim dataStructure As String
For Each Key In initialDict.Keys
dataStructure = TypeName(initialDict(Key))
Select Case dataStructure
Case "Dictionary"
Dim Key1 As Variant
For Each Key1 In initialDict(Key).Keys
Select Case TypeName(initialDict(Key)(Key1))
Case "String"
'Debug.Print Key & " " & Key1 & " " & initialDict(Key)(Key1) 'amount/currency/symbol
'because the Key1 (amount) is the same for each Key ("Amount_product", "Amount_product_subtotal", and so on; (see Json above) I needed to concatenate them to extract unique values
Select Case Key & "_" & Key1
'first set of values "Amount_Product"
Case "Amount_product_amount"
dAmount_product_amount = initialDict(Key)(Key1)
Case "Amount_product_currency"
sAmount_product_currency = initialDict(Key)(Key1)
Case "Amount_product_symbol"
sAmount_product_symbol = initialDict(Key)(Key1)
'second set of values "Amount_Product_Subtotal"
Case "Amount_product_subtotal_amount"
dAmount_product_subtotal_amount = initialDict(Key)(Key1)
Case "Amount_product_subtotal_currency"
sAmount_product_subtotal_currency = initialDict(Key)(Key1)
Case "Amount_product_subtotal_symbol"
sAmount_product_subtotal_symbol = initialDict(Key)(Key1)
' third set of values, and so on
End Select
'Debug.Print Key & ": " & Key1
Case "Dictionary"
Dim Key2 As Variant
For Each Key2 In initialDict(Key)(Key1).Keys
'Debug.Print TypeName(initialDict(key)(Key1)(Key2)) 'strings and one dict
Select Case TypeName(initialDict(Key)(Key1)(Key2))
Case "String"
Debug.Print Key & " " & Key1 & " " & Key2 & " " & initialDict(Key)(Key1)(Key2)
Case "Dictionary"
Dim Key3 As Variant
For Each Key3 In initialDict(Key)(Key1)(Key2).Keys
'Debug.Print TypeName(initialDict(key)(Key1)(Key2)(Key3)) 'string only
Debug.Print initialDict(Key)(Key1)(Key2)(Key3)
Next Key3
End Select
Next Key2
Case Else
MsgBox "Oops I missed this one"
End Select
Next Key1
Case "String", "Boolean", "Double"
Debug.Print Key & " : " & initialDict(Key)
Case "Collection"
'Debug.Print TypeName(initialDict(key)(1)) 'returns 1 Dict
Dim Key4 As Variant
For Each Key4 In initialDict(Key)(1).Keys 'Debug.Print TypeName(initialDict(key)(1)(Key4)) 'returns a dictionary
Dim Key5 As Variant
For Each Key5 In initialDict(Key)(1)(Key4).Keys ' Debug.Print TypeName(initialDict(key)(1)(Key4)(Key5)) returns 4 dictionaries
Dim Key6 As Variant
For Each Key6 In initialDict(Key)(1)(Key4)(Key5).Keys 'returns string
Debug.Print Key & " " & Key4 & " " & Key5 & " " & Key6 & " " & initialDict(Key)(1)(Key4)(Key5)(Key6)
Next Key6
Next Key5
Next Key4
Case Else
MsgBox "Oops I missed this one!"
End Select
Next Key
End Sub
I have the following json file with multiple Descriptions and Customers (not mydata but this is the general structure).
I would like a fast way to find out which customers have a specific module.
For example, the customers which have the AD Module are Customer3,Customer4 (valid are only rows in CustomersDescs and not in 'ALL')
{
"Descriptions": {
"AA": "AA-Module1",
"AD": "AD-Module2",
"AL": "AL-Module3",
"AAB": "AAB-Module4",
"AAC": "AAC-Module5",
"CE": "CE-Module6",
"CL": "CL-Module7"
},
"WebServices": {
"CA": "WS1",
"CB": "WS2",
"CL": "WS2",
"DB": "WS3",
"UA": "WS4"
},
"CustomersDescs": {
"ALL": [
"AA",
"AD",
"AL",
"AAB",
"AAC",
"CE",
"CL"
],
"Customer1": [
"AA",
"AAC"
],
"Customer2": [
"AA",
"CE"
],
"Customer3": [
"AA",
"CE",
"AD",
"CL"
],
"Customer4": [
"AA",
"CE",
"AD",
"CL"
]
}
}
Is there any way to retrieve this info ?
Thanks in advance
The below example shows how to parse JSON and get the data which customers have a specific module. Import JSON.bas module into the VBA project for JSON processing. Put the below code into VBA Project standard module:
Option Explicit
Sub TestModulesDistribution()
Dim sJSONString As String
Dim vJSON As Variant
Dim sState As String
Dim oModules As Object
Dim sCustomer As Variant
Dim sModule As Variant
' Read JSON content
' Put sourse JSON string to "\source.json" file in ASCII or Unicode charset
sJSONString = ReadTextFile(ThisWorkbook.Path & "\source.json", -2)
' Process JSON
JSON.Parse sJSONString, vJSON, sState
Set vJSON = vJSON("CustomersDescs")
Set oModules = CreateObject("Scripting.Dictionary")
vJSON.Remove "ALL"
For Each sCustomer In vJSON
For Each sModule In vJSON(sCustomer)
If Not oModules.Exists(sModule) Then Set oModules(sModule) = CreateObject("Scripting.Dictionary")
oModules(sModule)(sCustomer) = ""
Next
Next
' Output result for example
For Each sModule In oModules
Debug.Print sModule
Debug.Print vbTab & Join(oModules(sModule).Keys(), vbCrLf & vbTab)
Next
End Sub
Function ReadTextFile(sPath As String, lFormat As Long) As String
' lFormat -2 - System default, -1 - Unicode, 0 - ASCII
With CreateObject("Scripting.FileSystemObject").OpenTextFile(sPath, 1, False, lFormat)
ReadTextFile = ""
If Not .AtEndOfStream Then ReadTextFile = .ReadAll
.Close
End With
End Function
Put your JSON to the file in ASCII or Unicode charset to the same folder as the workbook, and name it source.json, then run TestModulesDistribution(). After processing, the example output for me is as follows:
AA
Customer1
Customer2
Customer3
Customer4
AAC
Customer1
CE
Customer2
Customer3
Customer4
AD
Customer3
Customer4
CL
Customer3
Customer4
You can access to oModules dictionary items by module names, and get sub dictionaries, each of them contains customer names as keys for that particular module name.
BTW, the similar approach applied in other answers.