VBA Noob here. Please excuse any gaps in terminology etc.
I am trying to parse a JSON file into a spreadsheet using VBA-JSON v2.2.3 (c) Tim Hall - https://github.com/VBA-tools/VBA-JSON.
The JSON file looks like this:
{
"site": "{5BEC7C29-FF95-4ECC-9314-064B52618EEE}",
"from": "2017-01-16",
"to": "2017-01-22",
"timeSheet": [
{
"date": "2017-01-16",
"person": "{E2A5FDE1-33F8-43CA-A01D-5DD4A3A5E23A}",
"personName": "James Smith",
"company": "{B03CF7B3-0BE9-44B4-8E55-47782FDD87C0}",
"companyName": "Acme Company Ltd",
"minutes": "510",
"activities": [
{
"name": "Training",
"code": "TR",
"minutes": "240"
},
{
"name": "Administration",
"code": "AD",
"minutes": "150"
},
{
"name": "Payroll",
"code": "PR",
"minutes": "60"
},
{
"name": "Meal break",
"code": "",
"minutes": "60"
}
]
}
]
}
There may be any number of 'timeSheet' records, as well as any number of 'Activities' within each timeSheet including zero.
I want a row in the spreadsheet for each activity, with the name and other data outputted next to that days activities. Essentially showing a log of all the activities done, for how long and by who. To complicate issues, I still need the name etc outputting even if no activities are recorded. I will then fill with 'unallocated time' or something similar.
Below is as far as I have got (abridged), with an updated count of the activities occurring every loop. This feels a little hacky and doesn't give me what I am looking for, often adding additional rows and sometimes missing activities entirely.
i = 2
j = 1
activCount = CStr(JSON("timeSheet")(1)("activities").Count)
If activCount = 0 Then activCount = 1
ws.Cells(i, 1) = JSON("site")
ws.Cells(i, 2) = JSON("from")
ws.Cells(i, 3) = JSON("to")
For Each item In JSON("timeSheet")
For j = 1 To activCount
On Error Resume Next
ws.Cells(i, 4) = item("date")
ws.Cells(i, 5) = item("personName")
ws.Cells(i, 6) = item("companyName")
ws.Cells(i, 7) = item("minutes")
ws.Cells(i, 9) = item("activities")(j)("name")
ws.Cells(i, 10) = item("activities")(j)("code")
ws.Cells(i, 11) = item("activities")(j)("minutes")
activCount = CStr(JSON("timeSheet")(i)("activities").Count)
If activCount = 0 Then activCount = 1
i = i + 1
Next
Next
Can someone help? I have run out of ideas and have been working it for some time! Thank you. :)
This worked fine for me:
Sub TestJson2()
Dim ts, act
Dim Json As Object, c As Range
'reading json from a worksheet cell...
Set Json = JsonConverter.ParseJson(Range("A3").Value)
Set c = ActiveSheet.Range("C5")
'loop over timesheets
For Each ts In Json("timeSheet")
'loop over timesheet activities
For Each act In ts("activities")
c.Resize(1, 11).Value = Array(Json("site"), Json("from"), Json("to"), _
ts("date"), ts("personName"), ts("companyName"), _
ts("minutes"), act("name"), act("code"), _
act("minutes"))
Set c = c.Offset(1, 0)
Next act
Next ts
End Sub
Related
Sorry for my bad English(English is not my first language)
I want to execute , exit the "long" and enter the "short" at the same time when "short entry" is trigged. I tried below code.It only exit when hit Target and Stoploss.It doesn't exit the "long" at "short entry" and doesn't exit the "short" at "long entry".
Much appreciate any help!
strategy.entry (id="Long", long=strategy.long, when = ValidLong, comment ="Long" )
strategy.entry (id="Short", long=strategy.short, when=ValidShort, comment="Short" )
strategy.exit(id = "Long Exit", from_entry = "Long", limit =Target , loss = Stoploss , when = strategy.position_size > 0)
strategy.exit(id = "Short Exit", from_entry = "Short", limit = Target, loss = Stoploss, when = strategy.position_size < 0 )
I'm not sure how to fix the following error and am looking for some help in this code I found that I have attempted to adapt for use in application embedded VB.Net:
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[System.Object]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path 'error', line 1, position 9.
Below is the code That is generating this error
Dim OutputText As String
Dim Worksteps = JsonConvert.DeserializeObject(Of List(Of Object))(responseFromServer)
Dim token As JToken
Dim SheetName As String
Dim SheetStatus As String
Dim Completed As Boolean
For Each value As Object In Worksteps
token = JObject.Parse(value.ToString())
SheetName = token.SelectToken("name")
SheetStatus = token.SelectToken("status")
Dim parts() As String = SheetName.Split(" "c)
If parts(0) = "Sheet_1" Then
If SheetStatus = "COMPLETED" Then
Completed = True
Exit For
End If
End If
Next value
Select Case Completed
Case True
OutputText = "Sheet_1 Status is COMPLETED"
Case False
OutputText = "Sheet_1 Status is NOT COMPLETED"
End Select
Here is the JSON I'm attempting to parse. My assumption is that I somehow need to first parse for the Worksteps before trying to make a list of worksteps but I'm not sure how to do that.
{
"error": null,
"worksteps": [
{
"id": "_210504_125916572_029875",
"name": "Sheet_1 4/4",
"job": {
"id": "060671-21",
"name": "2021 Laramie High School"
},
"status": "COMPLETED",
"amountPlanned": 544,
"wastePlanned": 60,
"amountProduced": 169,
"wasteProduced": 69,
"deviceId": "114",
"types": [
"ConventionalPrinting"
],
"sequenceType": "SheetfedPrinting",
"start": "2021-05-05T09:46:06-05:00",
"end": "2021-05-05T09:48:38-05:00",
"startPlanned": "2021-05-05T07:52:22-05:00",
"endPlanned": null,
"setuptimePlanned": 0,
"prodtimePlanned": 0,
"actualTimes": [
{
"timeTypeGroupName": "Production time",
"timeTypeName": "Execution time",
"duration": 33
},
{
"timeTypeGroupName": "Production time",
"timeTypeName": "Setup time",
"duration": 79
},
{
"timeTypeGroupName": "Auxiliary time",
"timeTypeName": "Auxiliary time",
"duration": 40
},
{
"timeTypeGroupName": "",
"timeTypeName": "",
"duration": 0
}
]
},
{
"id": "_210506_072306983_020035",
"name": "Sheet_2 4/4",
"job": {
"id": "060671-21",
"name": "2021 Laramie High School"
},
"status": "WAITING",
"amountPlanned": 0,
"wastePlanned": 0,
"amountProduced": 0,
"wasteProduced": 0,
"deviceId": "XL106_Pool",
"types": [
"ConventionalPrinting"
],
"sequenceType": "SheetfedPrinting",
"start": null,
"end": null,
"startPlanned": null,
"endPlanned": null,
"setuptimePlanned": 0,
"prodtimePlanned": 0,
"actualTimes": null
}
]
}
Here is the solution I came up with to convert JSON sample above to XML and then extract the values for the sheet and status:
'Parse the JSON responseFromServer into an XMLDocument
Dim xdoc As New XmlDocument
' "job_worksteps" is the name being provided
xdoc = JsonConvert.DeserializeXmlNode(responseFromServer,"job_worksteps")
' Look in the XML to determine the if a specified sheet has been completed
Dim nodes As XmlNodeList = xdoc.DocumentElement.SelectNodes("/job_worksteps/worksteps")
Dim Completed As Boolean
Dim SheetName, SheetStatus, OutputText As String
Dim TargetSheet As String = "Sheet_" + triggerEvent.String2 ' Integer as string included in the Prinergy RBA Trigger HTTP Post
' Loop through the XML nodes to find the SheetName node "name" and SheetStatus node "status"
For Each node As XmlNode In nodes
SheetName = node.SelectSingleNode("name").InnerText
SheetStatus = node.SelectSingleNode("status").InnerText
Dim parts() As String = SheetName.Split(" ")
' Validate the TargetSheet name has a Status of "COMPLETED"
If parts(0) = TargetSheet Then
If SheetStatus = "COMPLETED" Then
Completed = True
Exit For
End If
End If
Next
' Format the output we want for this test
' (The final Boolean value will actually be used to trigger another function if it is True)
Select Case Completed
Case True
OutputText = "Sheet_1 Status is COMPLETED"
Case False
OutputText = "Sheet_1 Status is NOT COMPLETED"
End Select
Here is the resulting XML from some additional code to append it to the OutputText variable:
Sheet_1 Status is COMPLETED
<job_worksteps>
<error />
<worksteps>
<id>_210505_073301677_027121</id>
<name>Sheet_1 4/4</name>
<job>
<id>060671-21</id>
<name>2021 Laramie High School</name>
</job>
<status>COMPLETED</status>
<amountPlanned>544</amountPlanned>
<wastePlanned>60</wastePlanned>
<amountProduced>169</amountProduced>
<wasteProduced>69</wasteProduced>
<deviceId>114</deviceId>
<types>ConventionalPrinting</types>
<sequenceType>SheetfedPrinting</sequenceType>
<start>2021-05-05T09:46:06-05:00</start>
<end>2021-05-05T09:48:38-05:00</end>
<startPlanned>2021-05-05T07:52:22-05:00</startPlanned>
<endPlanned />
<setuptimePlanned>0</setuptimePlanned>
<prodtimePlanned>0</prodtimePlanned>
<actualTimes />
</worksteps>
<worksteps>
<id>_210505_073301714_027146</id>
<name>Sheet_2 4/4</name>
<job>
<id>060671-21</id>
<name>2021 Laramie High School</name>
</job>
<status>WAITING</status>
<amountPlanned>0</amountPlanned>
<wastePlanned>0</wastePlanned>
<amountProduced>0</amountProduced>
<wasteProduced>0</wasteProduced>
<deviceId>XL106_Pool</deviceId>
<types>ConventionalPrinting</types>
<sequenceType>SheetfedPrinting</sequenceType>
<start />
<end />
<startPlanned />
<endPlanned />
<setuptimePlanned>0</setuptimePlanned>
<prodtimePlanned>0</prodtimePlanned>
<actualTimes />
</worksteps>
</job_worksteps>
--
I have a json as:
mytestdata = {
"success": True,
"message": "",
"data": {
"totalCount": 95,
"goal": [
{
"user_id": 123455,
"user_email": "john.smith#test.com",
"user_first_name": "John",
"user_last_name": "Smith",
"people_goals": [
{
"goal_id": 545555,
"goal_name": "test goal name",
"goal_owner": "123455",
"goal_narrative": "",
"goal_type": {
"id": 1,
"name": "Team"
},
"goal_create_at": "1595874095",
"goal_modified_at": "1595874095",
"goal_created_by": "123455",
"goal_updated_by": "123455",
"goal_start_date": "1593561600",
"goal_target_date": "1601424000",
"goal_progress": "34",
"goal_progress_color": "#ff9933",
"goal_status": "1",
"goal_permission": "internal,team",
"goal_category": [],
"goal_owner_full_name": "John Smith",
"goal_team_id": "766754",
"goal_team_name": "",
"goal_workstreams": []
}
]
}
]
}
}
I am trying to display all details in "people_goals" along with "user_last_name", "user_first_name","user_email", "user_id" with json_normalize.
So far I am able to display "people_goals", "user_first_name","user_email" with the code
df2 = pd.json_normalize(data=mytestdata['data'], record_path=['goal', 'people_goals'],
meta=[['goal','user_first_name'], ['goal','user_last_name'], ['goal','user_email']], errors='ignore')
However I am having issue when trying to include ['goal', 'user_id'] in the meta=[]
The error is:
TypeError Traceback (most recent call last)
<ipython-input-192-b7a124a075a0> in <module>
7 df2 = pd.json_normalize(data=mytestdata['data'], record_path=['goal', 'people_goals'],
8 meta=[['goal','user_first_name'], ['goal','user_last_name'], ['goal','user_email'], ['goal','user_id']],
----> 9 errors='ignore')
10
11 # df2 = pd.json_normalize(data=mytestdata['data'], record_path=['goal', 'people_goals'])
The only difference I see for 'user_id' is that it is not a string
Am I missing something here?
Your code works on my platform. I've migrated away from using record_path and meta parameters for two reasons. a) they are difficult to work out b) there are compatibility issues between versions of pandas
Therefore I now use approach of use json_normalize() multiple times to progressively expand JSON. Or use pd.Series. Have included both as examples.
df = pd.json_normalize(data=mytestdata['data']).explode("goal")
df = pd.concat([df, df["goal"].apply(pd.Series)], axis=1).drop(columns="goal").explode("people_goals")
df = pd.concat([df, df["people_goals"].apply(pd.Series)], axis=1).drop(columns="people_goals")
df = pd.concat([df, df["goal_type"].apply(pd.Series)], axis=1).drop(columns="goal_type")
df.T
df2 = pd.json_normalize(pd.json_normalize(
pd.json_normalize(data=mytestdata['data']).explode("goal").to_dict(orient="records")
).explode("goal.people_goals").to_dict(orient="records"))
df2.T
print(df.T.to_string())
output
0
totalCount 95
user_id 123455
user_email john.smith#test.com
user_first_name John
user_last_name Smith
goal_id 545555
goal_name test goal name
goal_owner 123455
goal_narrative
goal_create_at 1595874095
goal_modified_at 1595874095
goal_created_by 123455
goal_updated_by 123455
goal_start_date 1593561600
goal_target_date 1601424000
goal_progress 34
goal_progress_color #ff9933
goal_status 1
goal_permission internal,team
goal_category []
goal_owner_full_name John Smith
goal_team_id 766754
goal_team_name
goal_workstreams []
id 1
name Team
Summary
I am trying to access the deep levels of nested JSON that comes back from the API that I am working with at the moment. Using VBA-JSON through this process has been pretty easy, but I've ran into a little road block. For the most part, I've got this library to work properly, but I get an error anytime I try to grab an item that is nested in the JSON response (further than 2 JSON levels). I am going to submit this post, and then I'll go and gather some of the error codes that I am getting. Off the top of my head, I know that I get a Run-time error '13' when I try to use the code below that doesn't work. I think (could be wrong) that I am smart enough to understand the type mismatch error, but I don't know how to fix it.
This Works
For i = 1 To 20
ActiveSheet.Cells(i + 1, 5) = Json("issues")(i)("key")
ActiveSheet.Cells(i + 1, 6) = Json("issues")(i)("id")
Next
Doesn't Work
For i = 1 To 20
ActiveSheet.Cells(i + 1, 7) = Json("fields")(i)("summary")
Next
None of These Worked Either
ActiveSheet.Cells(i + 1, 7) = Json("fields")(i)("summary")
ActiveSheet.Cells(i + 1, 7) = Json("fields")("summary")
ActiveSheet.Cells(i + 1, 7) = Json("issues")("fields")("summary")
ActiveSheet.Cells(i + 1, 7) = Json("fields")("assignee")(i)("name")
ActiveSheet.Cells(i + 1, 7) = Json("issues")("fields")("assignee")(i)("name")
Updated with Some JSON
I edited the JSON that comes back for privacy reasons (obviously).
{
"expand": "schema,names",
"startAt": 0,
"maxResults": 50,
"total": 28,
"issues": [ {
"expand": "",
"id": "94581",
"self": "",
"key": "",
"fields": {
, "assignee": {}
, "status": {}
, "votes": {}
, "summary": ""
,
, "issuetype": {}
, "timespent": null,
}
}
,
{}
,
{}
,
{}
]
}
According to this data structure, my code should be looking at the right place, right?
Thoughts? :)
PS: Sorry if jargon is off, I'm a developer by hobby, not by job. :)
The Problem
Nested arrays were actually the problem. The 2'nd level of JSON items were not arrays, while, "fields" was.
To solve the problem, we need to add a (i + 1) to the code.
Working Code Example
''''''''
' Loop '
''''''''
For i = 1 To 20
ActiveSheet.Cells(i + 1, 5) = Json("issues")(i)("key")
ActiveSheet.Cells(i + 1, 6) = Json("issues")(i)("id")
ActiveSheet.Cells(i + 1, 7) = Json("issues")(i + 1)("fields")("summary")
ActiveSheet.Cells(i + 1, 8) = Json("issues")(i + 1)("fields")("watches")("watchCount")
ActiveSheet.Cells(i + 1, 9) = Json("issues")(i + 1)("fields")("workratio")
Next
Update :(
So, I got all of my code setup to run my report, and it was working, and then it randomly stops (I watched it do this on line 16 or so) and gives me another type mismatch error.
How were the rows 1-15 working, and then suddenly there is a type mismatch error?
lol...
Debugging now...
Update 2 :)
Simple mix up with the code structure, there wasn't a problem and the solution that I posted below works correctly.
So I'm trying to pull data from a JSON string (as seen below). When I decode the JSON using the code below, and then attempt to index the duration text, I get a nil return. I have tried everything and nothing seems to work.
Here is the Google Distance Matrix API JSON:
{
"destination_addresses" : [ "San Francisco, CA, USA" ],
"origin_addresses" : [ "Seattle, WA, USA" ],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "1,299 km",
"value" : 1299026
},
"duration" : {
"text" : "12 hours 18 mins",
"value" : 44303
},
"status" : "OK"
}]
}],
"status" : "OK"
}
And here is my code:
local json = require ("json")
local http = require("socket.http")
local myNewData1 = {}
local SaveData1 = function (event)
distanceReturn = ""
distance = ""
local URL1 = "http://maps.googleapis.com/maps/api/distancematrix/json?origins=Seattle&destinations=San+Francisco&mode=driving&&sensor=false"
local response1 = http.request(URL1)
local data2 = json.decode(response1)
if response1 == nil then
native.showAlert( "Data is nill", { "OK"})
print("Error1")
distanceReturn = "Error1"
elseif data2 == nill then
distanceReturn = "Error2"
native.showAlert( "Data is nill", { "OK"})
print("Error2")
else
for i = 1, #data2 do
print("Working")
print(data2[i].rows)
for j = 1, #data2[i].rows, 1 do
print("\t" .. data2[i].rows[j])
for k = 1, #data2[i].rows[k].elements, 1 do
print("\t" .. data2[i].rows[j].elements[k])
for g = 1, #data2[i].rows[k].elements[k].duration, 1 do
print("\t" .. data2[i].rows[k].elements[k].duration[g])
for f = 1, #data2[i].rows[k].elements[k].duration[g].text, 1 do
print("\t" .. data2[i].rows[k].elements[k].duration[g].text)
distance = data2[i].rows[k].elements[k].duration[g].text
distanceReturn = data2[i].rows[k].elements[k].duration[g].text
end
end
end
end
end
end
timer.performWithDelay (100, SaveData1, 999999)
Your loops are not correct. Try this shorter solution.
Replace all your "for i = 1, #data2 do" loop for this one below:
print("Working")
for i,row in ipairs(data2.rows) do
for j,element in ipairs(row.elements) do
print(element.duration.text)
end
end
This question was solved on Corona Forums by Rob Miracle (http://forums.coronalabs.com/topic/47319-parsing-json-from-google-distance-matrix-api/?hl=print_r#entry244400). The solution is simple:
"JSON and Lua tables are almost identical data structures. In this case your table data2 has top level entries:
data2.destination_addresses
data2.origin_addresses
data2.rows
data2.status
Now data2.rows is another table that is indexed by numbers (the [] brackets) but here is only one of them, but its still an array entry:
data.rows[1]
Then inside of it is another numerically indexed table called elements.
So far to get to the element they are (again there is only one of them
data2.rows[1].elements[1]
then it's just accessing the remaining elements:
data2.rows[1].elements[1].distance.text
data2.rows[1].elements[1].distance.value
data2.rows[1].elements[1].duration.text
data2.rows[1].elements[1].duration.value
There is a great table printing function called print_r which can be found in the community code which is great for dumping tables like this to see their structure."