VBA-Json Parse Nested Json - json
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
Related
create JSON using JSONCOVERTER
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
Integration of web API into Excel using Macro & VBA
I have used link - Parsing JSON to Excel using VBA to solve my problem, but it is not resolved fully. Up to JSON Parse it is working as expected then not able to convert it into 2D Array & that's why not able convert JSON data into Excel table. using code as below, Option Explicit Sub GetAPI_Data() Dim sJSONString As String Dim sJSONStringTmp1 As String Dim sJSONStringTmp2 As String Dim vJSON Dim sState As String Dim aData() Dim aHeader() ' Retrieve JSON content With CreateObject("MSXML2.XMLHTTP") .Open "GET", "https://pastebin.com/raw/Zp0mFEqd", True .send Do Until .readyState = 4: DoEvents: Loop sJSONString = "{ ""EmployeeDetails"": " + .responseText + "}" Debug.Print sJSONString End With Debug.Print sJSONString ' Parse JSON sample JSON.Parse sJSONString, vJSON, sState If sState = "Error" Then MsgBox "Invalid JSON": End ' Convert JSON to 2D Array JSON.toArray vJSON("EmployeeDetails"), aData, aHeader ' Output to worksheet #1 Output aHeader, aData, ThisWorkbook.Sheets(1) MsgBox "Completed" End Sub Sub Output(aHeader, aData, oDestWorksheet As Worksheet) With oDestWorksheet .Activate .Cells.Delete With .Cells(1, 1) .Resize(1, UBound(aHeader) - LBound(aHeader) + 1).Value = aHeader .Offset(1, 0).Resize( _ UBound(aData, 1) - LBound(aData, 1) + 1, _ UBound(aData, 2) - LBound(aData, 2) + 1 _ ).Value = aData End With .Columns.AutoFit End With End Sub My JSON Data as follows, { "EmployeeDetails": "[{\"AccountName\":\"CWT COMMODITIES (ANTWERP) N.V.\",\"AccountOwner\":null,\"Age\":\"257\",\"AgreementLevel\":null,\"Amount\":\"1\",\"Amount_converted\":\"1.13\",\"Amount_converted_Currency\":null,\"AmountCurrency\":\"EUR\",\"CloseDate\":\"2022-06-15\",\"CloseMonth\":null,\"CoreTechnology\":null,\"CreatedDate\":\"2021-10-01T07:52:36.000+0000\",\"CustomerIndustry\":\"Infrastructure / Transport\",\"District\":null,\"ePSFBranch_Location\":null,\"ExclusiveHBSTechnology\":null,\"ExpectedProjectDuration\":null,\"FiscalPeriod_Num\":\"6\",\"FiscalYear\":\"2022\",\"ForecastCategory\":\"Pipeline\",\"FPXBranch\":null,\"GrossMargin_Percentage\":null,\"Industry\":\"Education\",\"IndustryCode\":null,\"LeadSource\":null,\"LegacyOpportunityNumber\":null,\"LineofBusiness\":null,\"NextSteps\":null,\"OpportunityName\":\"CWT Onderhoud BRANDDETECTIE\",\"OpportunityOwner\":\"Wim Hespel\",\"OpportunityType\":null,\"OwnerRole\":\"Direct EUR VSK&TTG Sales\",\"PrimarySolutionFamily\":null,\"PrimarySubSolutionFamily\":null,\"Probability_Percentage\":\"5\",\"ProjectEndDate\":\"2022-06-15\",\"ProjectStartDate\":\"2022-06-15\",\"RecordType\":\"Core\",\"Region\":\"Europe\",\"SalesRegion\":\"Belgium & Luxembourg\",\"Stage\":\"1.First Calls\",\"SubRegion\":\"HBS Benelux\",\"OpportunityNumber\":\"0001458471\",\"VerticalMarket\":\"Infrastructure / Transport excluding Airports\",\"Win_LossCategory\":null,\"Win_LossReason\":null,\"Country\":\"Belgium\",\"InitiatedCPQEstimateProcess\":\"False\",\"LastModifiedDate\":\"2022-03-17T15:27:33.000+0000\",\"LocationSS\":null,\"OpportunityCurrency\":null,\"OpportunityID\":\"0065a0000109AMQAA2\",\"OpportunitySubType\":null,\"OwnerID\":\"0051H00000AvuQ2QAJ\",\"RecordTypeId\":\"0121H000001eZ9VQAU\",\"CustomerType\":\"Existing Customer\",\"GBE\":\"HBS\",\"EditedBy\":\"\",\"Field_Or_Event\":\"\",\"OldValue\":\"\",\"NewValue\":\"\",\"EditDate\":\"\",\"LastStageChangeDate\":null,\"StageDuration\":null,\"ExpectedRevenue\":\"0.05\",\"GrossMarginAtSubmission\":null,\"LastActivity\":null,\"OwnerEID\":\"H185118\"}]" } Error: 1] on local machine I am getting error in JSON.toArray i.e. not able to create 2D array. 2] while using above code with online JSON Data as per URL then getting only 2 column data which is not proper. Updated Code Option Explicit Sub GetAPI_Data() Dim sJSONString As String Dim sJSONStringTmp1 As String Dim sJSONStringTmp2 As String Dim vJSON Dim s Dim sState As String Dim aData() Dim aHeader() ' Retrieve JSON content With CreateObject("MSXML2.XMLHTTP") .Open "GET", "https://pastebin.com/raw/Zp0mFEqd", True .send Do Until .readyState = 4: DoEvents: Loop 'sJSONString = "{ ""EmployeeDetails"": " + .responseText + "}" sJSONString = .responseText Debug.Print sJSONString End With ' Parse JSON sample JSON.Parse sJSONString, vJSON, sState If sState = "Error" Then MsgBox "Invalid JSON": End Debug.Print vJSON.Item("EmployeeDetails") 'vJSON("EmployeeDetails") = "{ ""EmployeeDetails"": " + vJSON("EmployeeDetails") + "}" s = vJSON("EmployeeDetails") s = "{""data"":" & s & "}" Debug.Print vJSON.Item("EmployeeDetails") Dim xJSON As Dictionary 'JSON.Parse vJSON("EmployeeDetails"), xJSON, sState JSON.Parse s, xJSON, sState If sState = "Error" Then MsgBox "Invalid JSON": End ' Convert JSON to 2D Array JSON.toArray xJSON, aData, aHeader ' Output to worksheet #1 Output aHeader, aData, ThisWorkbook.Sheets(1) MsgBox "Completed" End Sub Sub Output(aHeader, aData, oDestWorksheet As Worksheet) With oDestWorksheet .Activate .Cells.Delete With .Cells(1, 1) .Resize(1, UBound(aHeader) - LBound(aHeader) + 1).Value = aHeader .Offset(1, 0).Resize( _ UBound(aData, 1) - LBound(aData, 1) + 1, _ UBound(aData, 2) - LBound(aData, 2) + 1 _ ).Value = aData End With .Columns.AutoFit End With End Sub Note : I have updated API with multiple line of JSON Error: 1] Now I am getting required data. 2] But the main issue is, it is coming only in 2 rows (1 for column header & other one for Data) 3] Requirement is, it should display 5 different rows with first row of header Please help me out from this.
This worked for me to give a 2D array which could be placed on a worksheet: Sub Tester() Dim json As Object, s As String, recs As Object, arr Set json = ParseJson(GetContent("C:\Temp\json.txt")) 'reading from a file for testing s = json("EmployeeDetails") 'get the embedded json Set json = ParseJson("{""data"":" & s & "}") 'parse the embedded json Set recs = json("data") 'collection of records 'a Collection of records arr = RecsToArray(recs) 'convert to a 2D array With Sheet6.Range("A1") .Resize(UBound(arr, 1), UBound(arr, 2)).Value = arr 'write array to sheet End With End Sub 'Convert an array/collection of json objects (dictionaries) ' to a tabular 2D array, with a header row Function RecsToArray(recs As Collection) Dim rec, k, i As Long, r As Long, c As Long, arr() Dim dictCols As Object Set dictCols = CreateObject("scripting.dictionary") i = 0 'Collect all field names (checking every record in case some may be either incomplete or contain "extra" fields) ' Assumes all field names are unique per record, and no nested objects/arrays within a record For Each rec In recs For Each k In rec If Not dictCols.Exists(k) Then i = i + 1 dictCols.Add k, i End If Next k Next rec 'size the output array ReDim arr(1 To recs.Count + 1, 1 To i) 'Populate the header row For Each k In dictCols arr(1, dictCols(k)) = k Next k r = 1 'collect the data rows For Each rec In recs r = r + 1 'next output row For Each k In rec arr(r, dictCols(k)) = rec(k) Next k Next rec RecsToArray = arr End Function Function GetContent(f As String) As String GetContent = CreateObject("scripting.filesystemobject"). _ OpenTextFile(f, 1).ReadAll() End Function
The very first issue you have is that you put an additional { "EmployeeDetails" …json… } around your JSON that allready has this sJSONString = "{ ""EmployeeDetails"": " + .responseText + "}" Don't do that! Second issue you have is that you have a string encoded JSON inside a JSON: So your original JSON is: { "EmployeeDetails": "[{\"AccountName\":\"CWT COMMODITIES (ANTWERP) N.V.\",\"AccountOwner\":null,\"Age\":\"257\",\"AgreementLevel\":null,\"Amount\":\"1\",\"Amount_converted\":\"1.13\",\"Amount_converted_Currency\":null,\"AmountCurrency\":\"EUR\",\"CloseDate\":\"2022-06-15\",\"CloseMonth\":null,\"CoreTechnology\":null,\"CreatedDate\":\"2021-10-01T07:52:36.000+0000\",\"CustomerIndustry\":\"Infrastructure / Transport\",\"District\":null,\"ePSFBranch_Location\":null,\"ExclusiveHBSTechnology\":null,\"ExpectedProjectDuration\":null,\"FiscalPeriod_Num\":\"6\",\"FiscalYear\":\"2022\",\"ForecastCategory\":\"Pipeline\",\"FPXBranch\":null,\"GrossMargin_Percentage\":null,\"Industry\":\"Education\",\"IndustryCode\":null,\"LeadSource\":null,\"LegacyOpportunityNumber\":null,\"LineofBusiness\":null,\"NextSteps\":null,\"OpportunityName\":\"CWT Onderhoud BRANDDETECTIE\",\"OpportunityOwner\":\"Wim Hespel\",\"OpportunityType\":null,\"OwnerRole\":\"Direct EUR VSK&TTG Sales\",\"PrimarySolutionFamily\":null,\"PrimarySubSolutionFamily\":null,\"Probability_Percentage\":\"5\",\"ProjectEndDate\":\"2022-06-15\",\"ProjectStartDate\":\"2022-06-15\",\"RecordType\":\"Core\",\"Region\":\"Europe\",\"SalesRegion\":\"Belgium & Luxembourg\",\"Stage\":\"1.First Calls\",\"SubRegion\":\"HBS Benelux\",\"OpportunityNumber\":\"0001458471\",\"VerticalMarket\":\"Infrastructure / Transport excluding Airports\",\"Win_LossCategory\":null,\"Win_LossReason\":null,\"Country\":\"Belgium\",\"InitiatedCPQEstimateProcess\":\"False\",\"LastModifiedDate\":\"2022-03-17T15:27:33.000+0000\",\"LocationSS\":null,\"OpportunityCurrency\":null,\"OpportunityID\":\"0065a0000109AMQAA2\",\"OpportunitySubType\":null,\"OwnerID\":\"0051H00000AvuQ2QAJ\",\"RecordTypeId\":\"0121H000001eZ9VQAU\",\"CustomerType\":\"Existing Customer\",\"GBE\":\"HBS\",\"EditedBy\":\"\",\"Field_Or_Event\":\"\",\"OldValue\":\"\",\"NewValue\":\"\",\"EditDate\":\"\",\"LastStageChangeDate\":null,\"StageDuration\":null,\"ExpectedRevenue\":\"0.05\",\"GrossMarginAtSubmission\":null,\"LastActivity\":null,\"OwnerEID\":\"H185118\"}]" } and what you get out of vJSON.Item("EmployeeDetails") is [ { "AccountName": "CWT COMMODITIES (ANTWERP) N.V.", "AccountOwner": null, "Age": "257", "AgreementLevel": null, "Amount": "1", "Amount_converted": "1.13", "Amount_converted_Currency": null, "AmountCurrency": "EUR", "CloseDate": "2022-06-15", "CloseMonth": null, "CoreTechnology": null, "CreatedDate": "2021-10-01T07:52:36.000+0000", "CustomerIndustry": "Infrastructure / Transport", "District": null, "ePSFBranch_Location": null, "ExclusiveHBSTechnology": null, "ExpectedProjectDuration": null, "FiscalPeriod_Num": "6", "FiscalYear": "2022", "ForecastCategory": "Pipeline", "FPXBranch": null, "GrossMargin_Percentage": null, "Industry": "Education", "IndustryCode": null, "LeadSource": null, "LegacyOpportunityNumber": null, "LineofBusiness": null, "NextSteps": null, "OpportunityName": "CWT Onderhoud BRANDDETECTIE", "OpportunityOwner": "Wim Hespel", "OpportunityType": null, "OwnerRole": "Direct EUR VSK&TTG Sales", "PrimarySolutionFamily": null, "PrimarySubSolutionFamily": null, "Probability_Percentage": "5", "ProjectEndDate": "2022-06-15", "ProjectStartDate": "2022-06-15", "RecordType": "Core", "Region": "Europe", "SalesRegion": "Belgium & Luxembourg", "Stage": "1.First Calls", "SubRegion": "HBS Benelux", "OpportunityNumber": "0001458471", "VerticalMarket": "Infrastructure / Transport excluding Airports", "Win_LossCategory": null, "Win_LossReason": null, "Country": "Belgium", "InitiatedCPQEstimateProcess": "False", "LastModifiedDate": "2022-03-17T15:27:33.000+0000", "LocationSS": null, "OpportunityCurrency": null, "OpportunityID": "0065a0000109AMQAA2", "OpportunitySubType": null, "OwnerID": "0051H00000AvuQ2QAJ", "RecordTypeId": "0121H000001eZ9VQAU", "CustomerType": "Existing Customer", "GBE": "HBS", "EditedBy": "", "Field_Or_Event": "", "OldValue": "", "NewValue": "", "EditDate": "", "LastStageChangeDate": null, "StageDuration": null, "ExpectedRevenue": "0.05", "GrossMarginAtSubmission": null, "LastActivity": null, "OwnerEID": "H185118" } ] Which you will need to parse again because this still is JSON! But the converter you use does not accept the JSON to start with [ and thats another issue here. Because if I strip that brackets off so the [ ] in the beginning and end are gone and parse that again it will work: Sub GetAPI_Data() Dim sJSONString As String Dim sJSONStringTmp1 As String Dim sJSONStringTmp2 As String Dim vJSON As Dictionary Dim sState As String Dim aData() Dim aHeader() ' Retrieve JSON content With CreateObject("MSXML2.XMLHTTP") .Open "GET", "https://pastebin.com/raw/Zp0mFEqd", True .send Do Until .readyState = 4: DoEvents: Loop 'sJSONString = "{ ""EmployeeDetails"": " + .responseText + "}" 'don't do this! sJSONString = .responseText End With Debug.Print sJSONString ' Parse JSON sample JSON.Parse sJSONString, vJSON, sState If sState = "Error" Then MsgBox "Invalid JSON": End Debug.Print vJSON.Item("EmployeeDetails") Dim StripOffOuterBrackets As String StripOffOuterBrackets = Mid(vJSON.Item("EmployeeDetails"), 2, Len(vJSON.Item("EmployeeDetails")) - 2) Debug.Print StripOffOuterBrackets Dim xJSON As Dictionary JSON.Parse StripOffOuterBrackets, xJSON, sState If sState = "Error" Then MsgBox "Invalid JSON": End ' Convert JSON to 2D Array JSON.ToArray xJSON, aData, aHeader ' Output to worksheet #1 Output aHeader, aData, ThisWorkbook.Sheets(1) MsgBox "Completed" End Sub And it outputs the following (and some more lines)
Loop JSON in array for same value in VBA
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.
How to simplify this code using newtonsoft or other json parsers?
I got a simple json {"200567175963759": { "pair": "esp_btc", "type": "sell", "amount": 2000000, "rate": 1E-08, "timestamp_created": "1498114417", "status": 0}} I want to parse it without creating any new classes. I want to make it easy. I am using jsonhelper class that I created my self to parse it. It's basically try to find the first thing between two double quotes and got 200567175963759 which is the order id. Get parameter is simply finding something between "pair":" and " For simple json it works fine. How can I do get order ID, which si 200567175963759, or timestamp, using better parser, like newtonsoft. I wonder if I can do that using newtonsoft json? Dim jsonstring = jsonHelper.stripWhiteSpace(order3.ToString) '{"200567175963759": { "pair": "esp_btc", "type": "sell", "amount": 2000000, "rate": 1E-08, "timestamp_created": "1498114417", "status": 0}} Dim orderid = fGetToken(order3.ToString, 1, """", """") Dim base = b Dim quote = key Dim typeOfOrder = jsonHelper.getParameter(jsonstring, "type") Dim amount = jsonHelper.getParameter(jsonstring, "amount") Dim rate = jsonHelper.getParameter(jsonstring, "rate") Dim timestamp_created = jsonHelper.getParameter(jsonstring, "timestamp_created") Dim order4 = OrdersAtExchange.createOrders(amount, base, quote, _exchange, timestamp_created, rate, orderid) _orders.Add(order4) If I try to parse that using newtonsoft, I got this object whose type is Dim order = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonorders) Dim order1 = CType(order, Newtonsoft.Json.Linq.JObject) Dim order2 = order1.Item("return").ToList I look at all the method in Newtonsoft.Json.Linq.JObject I can't find anything that say convert dictionary structures in json to say generic.dictionary There is something like that. I tried but simply didn't work. So I wonder if there's an actual sample of some code parsing that simple json with newtonsoft?
Object is Type Dictionary, In case if property looks like index or key it probably dictionary Dim JsonString As String = "{""200567175963759"": { ""pair"": ""esp_btc"", ""type"": ""sell"", ""amount"": 2000000, ""rate"": 1E-08, ""timestamp_created"": ""1498114417"", ""status"": 0}}" Dim JsonSettings = New Newtonsoft.Json.JsonSerializerSettings JsonSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore Dim OutObject = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Dictionary(Of String, SampleType))(JsonString) Class SampleType Property pair As String Property type As String Property amount As String Property rate As String Property timestamp_created As String Property status As String End Class
Here is a sample showing how you can parse your JSON using Json.Net's LINQ-to-JSON API (JTokens, JObjects, etc.) Dim json As String = "{" & " ""200567175963759"": {" & " ""pair"": ""esp_btc""," & " ""type"": ""sell""," & " ""amount"": 2000000," & " ""rate"": 1E-08," & " ""timestamp_created"": ""1498114417""," & " ""status"": 0" & " }" & "}" Dim rootObject As JObject = JObject.Parse(json) For Each prop As JProperty In rootObject.Properties() Dim orderid As String = prop.Name Dim orderInfo As JObject = prop.Value Dim pair As String = orderInfo("pair").ToString() Dim typeOfOrder As String = orderInfo("type").ToString() Dim amount As Decimal = orderInfo("amount").ToObject(Of Decimal) Dim rate As Decimal = orderInfo("rate").ToObject(Of Decimal) Dim timestamp_created As String = orderInfo("timestamp_created").ToString() Dim status As Integer = orderInfo("status").ToObject(Of Integer) 'etc. ... Next Demo: https://dotnetfiddle.net/X9SPIE
How can I retrieve info from a specific json file
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.