Navigating JSON in Python? - python

ANSWER: The square brackets inside data["data"]["items"] indicates a list of dictionaries. I had thought that the brackets indicated that all of those dictionaries were a list inside a list of one item (i.e. [[item1, item2]]) so I had to call a 0 index like so: data["data"]["items"][0]. That was incorrect, the [0] is unecessary, which I really should have known, but didn't realize.
I am using GAE, so you can think of the self.response.write statement as a print.
For some reason, although the user_xid prints correctly, the interpreter is giving me the error when I try to parse json_data["data"]["items"][0] in my for loop, it appears to be evaluating x as a string, because I am getting an error telling me that string indicies must be integers.
I didn't realize json_data["data"]["items"][0] would yield a string. I'm somewhat stymied. Would it have anything to do with using json_data["data"]["items"][0] as opposed to json_data["data"]["items"]?
I have also tried json.loads(json_data["data"]["items"][0]) to no avail. Any input is appreciated.
I have the following JSON, which I have broken out a bit to make it easier to read:
{"meta":
{"user_xid": "REDACTED", "message": "OK", "code": 200, "time": 1436273692},
"data":
{"items": [
{"time_completed": 1436193938, "xid": "4tLz2M3x8_E9mTPIQsQUdrj-83spikha", "title": "for 7h 40m", "time_created": 1436165067, "time_updated": 1436194828, "details":
{"body": 0, "sound": 14348, "tz": "America/Vancouver", "awakenings": 1, "light": 13258, "mind": 0, "asleep_time": 1436165760, "awake": 1265, "rem": 0, "duration": 28871, "smart_alarm_fire": 1436193000, "quality": 81, "awake_time": 1436193600, "sunrise": 1436184840, "sunset": 1436156340},
"date": 20150706, "shared": true, "sub_type": 0},
{"time_completed": 1436135400, "xid": "4tLz2M3x8_Hk71lnuSLbeq4ascwJg-J8", "title": "for 25m", "time_created": 1436133600, "time_updated": 1436135590, "details":
{"body": 0, "sound": 0, "tz": "America/Vancouver", "awakenings": 0, "light": 1500, "mind": 0, "asleep_time": 1436133600, "awake": 300, "rem": 0, "duration": 1800, "smart_alarm_fire": 0, "quality": 2, "awake_time": 1436134800, "sunrise": 0, "sunset": 0},
"date": 20150705, "shared": true, "sub_type": 2},
{"time_completed": 1436133497, "xid": "4tLz2M3x8_GIYf6G9_ZKH9xrKpuEzMjm", "title": "for 23m", "time_created": 1436131387, "time_updated": 1436135461, "details":
{"body": 0, "sound": 1391, "tz": "America/Vancouver", "awakenings": 0, "light": 0, "mind": 0, "asleep_time": 1436132100, "awake": 719, "rem": 0, "duration": 2110, "smart_alarm_fire": 1436133445, "quality": 4, "awake_time": 1436133300, "sunrise": 0, "sunset": 0},
"date": 20150705, "shared": true, "sub_type": 1},
{"time_completed": 1436117080, "xid": "4tLz2M3x8_EUZyqCoXYOJO7Hfe92eSqg", "title": "for 26m","time_created": 1436115061, "time_updated": 1436121619, "details":
{"body": 0, "sound": 480, "tz": "America/Vancouver", "awakenings": 0, "light": 1119, "mind": 0, "asleep_time": 1436115479, "awake": 420, "rem": 0, "duration": 2019, "smart_alarm_fire": 1436117069, "quality": 2, "awake_time": 1436116800, "sunrise": 1436098380, "sunset": 1436156280},
"date": 20150705, "shared": true, "sub_type": 1},
],
"links":
{"next": "REDACTED"},
"size": 10}
}
I am using the following in Python:
json_data = json.loads(user_data)
self.response.write('<br><br>x_id: ' + json_data["meta"]["user_xid"])
i = 0
for x in json_data["data"]["items"][0]:
duration = x['details']['duration']
date = x["date"]
self.response.write('<br><br>sleep number ' + str(i) + ' | duration: ' + str(duration) + ' | date: ' + str(date))
i += 1
I am using GAE, so you can think of the self.response.write statement as a print.
For some reason, although the user_xid prints correctly, the interpreter is giving me the error when I try to parse json_data["data"]["items"][0] in my for loop, it appears to be evaluating x as a string, because I am getting an error telling me that string indicies must be integers.
I didn't realize json_data["data"]["items"][0] would yield a string. I'm somewhat stymied. Would it have anything to do with using json_data["data"]["items"][0] as opposed to json_data["data"]["items"]?
I have also tried json.loads(json_data["data"]["items"][0]) to no avail. Any input is appreciated.
Thanks!

By doing some basic debugging and data examination you will find that the use of the [0] is incorrect
I quickly loaded that data and tried it
In [11]: for i in x['data']['items'][0]:
....: print i
....:
time_completed
xid
details
title
date
shared
sub_type
time_created
time_updated
And this is effectively what you got, but not what you expected.
So what is going on? If you you do the following
x['data']['items'][0]
Out[28]:
{'date': 20150706,
'details': {'asleep_time': 1436165760,
'awake': 1265,
'awake_time': 1436193600,
'awakenings': 1,
'body': 0,
'duration': 28871,
'light': 13258,
'mind': 0,
'quality': 81,
'rem': 0,
'smart_alarm_fire': 1436193000,
'sound': 14348,
'sunrise': 1436184840,
'sunset': 1436156340,
'tz': 'America/Vancouver'},
'shared': True,
'sub_type': 0,
'time_completed': 1436193938,
'time_created': 1436165067,
'time_updated': 1436194828,
'title': 'for 7h 40m',
'xid': '4tLz2M3x8_E9mTPIQsQUdrj-83spikha'}
The result is as you expect.
The answer is because you are trying to iterate over x['data']['items'][0]
When you iterate over a dictionary you will get the keys not the values by default. Hence you are getting the unexpected string.
Where as the following code will give you what you want.
In [30]: for i in x['data']['items']:
print i['details']['duration']
....:
28871
1800
2110
2019
This iterates over each item (a dictioanary) in the list of items.
I would suggest the downvote is due to a large posting of code, and no real evidence of basic debugging.

Related

python and Json - editing file

So, I want to change my info in json file from python, but I am having trouble.
my json file is just info that I want to edit later:
[
{
"codigo": 10,
"Nom_articulo": "jabon",
"valor": 2500,
"cantidad": 6,
"subtotal": 0,
"descuento": 0
},
{
"codigo": 20,
"Nom_articulo": "Crema",
"valor": 9800,
"cantidad": 4,
"subtotal": 0,
"descuento": 0
},
{
"codigo": 30,
"Nom_articulo": "Cepillo",
"valor": 6000,
"cantidad": 7,
"subtotal": 0,
"descuento": 0
},
{
"codigo": 40,
"Nom_articulo": "Servilletas",
"valor": 3000,
"cantidad": 2,
"subtotal": 0,
"descuento": 0
},
{
"codigo": 50,
"Nom_articulo": "Desodorante",
"valor": 5000,
"cantidad": 6,
"subtotal": 0,
"descuento": 0
}
]
I want to change the value of "subtotal" in all my dictionaries.
so basically what I did was:
for i in range(len(archivo_r)):
precio= archivo_r[i]["valor"]
cantidad=archivo_r[i]["cantidad"]
subtotal=precio*cantidad
print(archivo_r[i]["codigo"], " - " ,archivo_r[i]["Nom_articulo"], " = ", str(subtotal))
#almacenar mis subtotales en el archivo json
print("sbtotal" ,archivo_r[i]["subtotal"])
archivo_r[i]["subtotal"]=subtotal
#archivo_r[i]["subtotal"].append(subtotal)
#print(archivo_r)
write_json(**XXXXX**)
This part of the code:
archivo_r[i]["subtotal"]=subtotal does exactly what I need, but (and this could be very silly, but I am a little lost here) I do not know how to use that to re-write my json file. I mean, I have the function to write it.
def write_json(info, nombre_archivo="productos.json"):
with open(nombre_archivo, "w") as p:
json.dump(info, p)
I need to pass the information in write_json(**XXXXX**), but have been trying to storage my archivo_r[i]["subtotal"]=subtotal in a variable to pass it and other things, but nothing work. I know I am doing wrong but not sure how to solve it.
Once you're done processing the data, simply pass archivo_r to your write_json() function and you should be fine.
As an aside, you can iterate directly over the JSON objects like so:
for section in archivo_r:
precio = section["valor"]
...
You can then replace all instances of archivo_r[i] with section, or whatever you want to call the variable.

TypeError: string indices must be integers while parsing a json

I'm parsing data from the fitbit API (https://dev.fitbit.com/build/reference/web-api/heart-rate/)
and keep getting TypeError: string indices must be integers. I understand, based off of reading other questions, that this could be due to dealing with lists of dictionaries. Right now, I'm parsing my json like this:
fat_burn = "{0:.2f}".format(activity_request.json()['activities-heart'][0]['value']['heartRateZones'][1]['minutes'])
My json looks like this:
{
"activities-heart": [
{
"dateTime": "2015-08-04",
"value": {
"customHeartRateZones": [],
"heartRateZones": [
{
"caloriesOut": 740.15264,
"max": 94,
"min": 30,
"minutes": 593,
"name": "Out of Range"
},
{
"caloriesOut": 249.66204,
"max": 132,
"min": 94,
"minutes": 46,
"name": "Fat Burn"
},
{
"caloriesOut": 0,
"max": 160,
"min": 132,
"minutes": 0,
"name": "Cardio"
},
{
"caloriesOut": 0,
"max": 220,
"min": 160,
"minutes": 0,
"name": "Peak"
}
],
"restingHeartRate": 68
}
}
]
}
I had an error that looked like TypeError: list indices must be integers or slices, not str when I was parsing the json like:
fat_burn = "{0:.2f}".format(activity_request.json()['activities-heart']['value']['heartRateZones'][1]['minutes'])
but managed to resolve it by adding a [0].
Why did I need to index the first entry to solve that error, and what do I need to do to resolve the error I'm having now?
activities-heart is a key who's value is a list. Lists are sequential and are indexed with integers (eg, 0, 1, 2, ...).
In your expression, you are trying perform a look up on the list using a string (value). This is where your TypeError is coming from.
Other elements in the data such as this..
{
"caloriesOut": 740.15264,
"max": 94,
"min": 30,
"minutes": 593,
"name": "Out of Range"
}
Are dictionaries which allow you to use any hashable values for the key. This is why you're able to use a string for the key.
https://www.geeksforgeeks.org/difference-between-list-and-dictionary-in-python
See, let's store your data in a variable a, then,
When you are doing this, a['activities-heart']:
You will get an output like this:
[{'dateTime': '2015-08-04',
'value': {'customHeartRateZones': [],
'heartRateZones': [{'caloriesOut': 740.15264,
'max': 94,
'min': 30,
'minutes': 593,
'name': 'Out of Range'},
{'caloriesOut': 249.66204,
'max': 132,
'min': 94,
'minutes': 46,
'name': 'Fat Burn'},
{'caloriesOut': 0, 'max': 160, 'min': 132, 'minutes': 0, 'name': 'Cardio'},
{'caloriesOut': 0, 'max': 220, 'min': 160, 'minutes': 0, 'name': 'Peak'}],
'restingHeartRate': 68}}]
This is a list and at 0 index is your value, therefore,
a['activities-heart'][0] will give you a dictionary.
Therefore you can perform like this:
a['activities-heart'][0]['value']['heartRateZones'][1]['minutes']
Output:
46

update array in mongodb with new item and value

I am trying to update the array: ['media_details'] with a local image path after the image has been downloaded. However using $push just added the local_url on top.
This is what ['media_details'] looks like:
"image_details": [
{
"processed": true,
"position": 0,
"seconds": "46",
"src_url": "https://xxxxx/1.jpg",
"image_fname": "1.jpg",
},
{
"processed": true,
"position": 1,
"seconds": "55",
"src_url": "https://xxxxx/2.jpg",
"image_fname": "2.jpg",
},
my code then downloads the image from the src_url and I want to add the local image url to the ['media_details'].
job = mongo.db.JobProcess
job.update({'_id': db_id},
{'$push': {
'image_details': {
'local_url': img_local_file,
}
}})
This adds the local_url to the top of the ['media_details'] - like so:
{'local_url': '/bin/static/5432ec0f-ea53-4fe1-83e4-f78166d1b9a6/1.jpg'},
{'local_url': '/bin/static/5432ec0f-ea53-4fe1-83e4-f78166d1b9a6/2.jpg'},
{'processed': True, 'position': 0, 'seconds': '46', 'src_url': 'https://xxxxx1.jpg', 'image_fname': '1.jpg'}
what I want it to do is:
"image_details": [
{
"processed": true,
"position": 0,
"seconds": "46",
"src_url": "https://xxxxx/1.jpg",
"image_fname": "1.jpg",
"local_url": "/bin/static/5432ec0f-ea53-4fe1-83e4-f78166d1b9a6/1.jpg"
},
but which command ($set, $push, $addToSet) is best suited for updating this? and how do I implement it?
You need to update the image_details array item using the positional operator $. You will need a query that can uniquely identify the array item, perhaps src_url:
job.update({$and:[
{"_id": db_id},
{"image_details.src_url": img_src_url }
]},
{$set :{"image_details.$.local_url": img_local_file },
{multi:false})
You need to use positional update operator
job.updateOne({
'_id': db_id,
'image_details.src_url': yourUrl,
}, {
$set: {
'image_details.$.local_url': img_local_file
});

JSON parse Python

I have a json data that i got from VK.
{
"response": [{
"id": 156603484,
"name": "Equestria in the Space",
"screen_name": "equestriaspace",
"is_closed": 0,
"type": "group",
"is_admin": 1,
"admin_level": 3,
"is_member": 1,
"description": "Официально сообщество Equestria in the Space!",
"photo_50": "https://pp.userap...089/u0_mBSE4E34.jpg",
"photo_100": "https://pp.userap...088/O6vENP0IW_w.jpg",
"photo_200": "https://pp.userap...086/rwntMz6YwWM.jpg"
}]
}
So i wanted to print only "name" but when i did it it gave me an error
TypeError: list indices must be integers or slices, not str
My code is:
method_url = 'https://api.vk.com/method/groups.getById?'
data = dict(access_token=access_token, gid=group_id)
response = requests.post(method_url, data)
result = json.loads(response.text)
print (result['response']['name'])
Any idea how can i fix it? In google i found how to parse json with one array. But here is two or something
P.S dont beat me so much. I am new in Python, just learning
What sort of data structure is the value of the key response?
i.e. how would you get it if I gave you the following instead?
"response": [{
"id": 156603484,
"name": "Equestria in the Space",
"screen_name": "equestriaspace",
"is_closed": 0,
"type": "group",
"is_admin": 1,
"admin_level": 3,
"is_member": 1,
"description": "Официально сообщество Equestria in the Space!",
"photo_50": "https://pp.userap...089/u0_mBSE4E34.jpg",
"photo_100": "https://pp.userap...088/O6vENP0IW_w.jpg",
"photo_200": "https://pp.userap...086/rwntMz6YwWM.jpg"
},
{
"not_a_real_response": "just some garbage actually"
}]
You would need to pick out the first response in that array of responses. As nice people in the comments have already told you.
name = result['response'][0]['name']

Google Sheets alternating colors via API

Using the sheets API with Python I'm attempting to format a sheet using alternating colors. In the UI this is found at Format > Alternating colors...
From what I've been able to find this is done via the API using banding. Unfortunately, I haven't been able to find a working example of how this is done. Below is the values dictionary I've constructed, color values aren't important at the moment, I'd just like it to colorize the sheet.
requests = {
'bandedRange': {
'bandedRangeId': 1,
'range': {
'sheetId': 0,
'startRowIndex': 0,
'endRowIndex': len(values),
'startColumnIndex': 0,
'endColumnIndex': 4,
},
'rowProperties': {
'headerColor': {
'red': 1,
'green': 0,
'blue': 1,
'alpha': 1,
},
'firstBandColor': {
'red': 1,
'green': 0,
'blue': 0,
'alpha': 0,
},
'secondBandColor': {
'red': 0,
'green': 1,
'blue': 0,
'alpha': 0,
}
},
},
'fields': '*',
}
body = {'requests': requests}
response = service.spreadsheets().batchUpdate(spreadsheetId=spreadsheet_id, body=body).execute()
This fails with the following error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib/python2.7/dist-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
return wrapped(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/googleapiclient/http.py", line 840, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://sheets.googleapis.com/v4/spreadsheets/$spreadsheet_id:batchUpdate?alt=json returned "Invalid JSON payload received. Unknown name "banded_range" at 'requests': Cannot find field.">
I'm fairly certain my issue is the fields value, but I can't find a valid example of what to use here. I get the same error if I omit the fields key entirely.
Per the reference docs for batchUpdate, requests takes an array of Request objects. Each Request must have exactly one field set, the available fields for banding being:
"updateBanding": {
object(UpdateBandingRequest)
},
"addBanding": {
object(AddBandingRequest)
},
"deleteBanding": {
object(DeleteBandingRequest)
},
There is no field bandedRange, which is what you're trying to set. That's what the error message (Unknown name "banded_range" at 'requests': Cannot find field.) is saying... though I have no idea why it translated bandedRange to snake_case.
Depending on if you want to add or update the banded range, you'd set either updateBanding with an UpdateBandingRequest object, or addBanding with an AddBandingRequest object.
By adding addBanding to your JSON format. As explained above you will end up creating the below JSON. Also, the key fields is optional.
{'addBanding': {
'bandedRange': {
'bandedRangeId': 1,
'range': {
'sheetId': 0,
'startRowIndex': 0,
'endRowIndex': len(values),
'startColumnIndex': 0,
'endColumnIndex': 4,
},
'rowProperties': {
'headerColor': {
'red': 1,
'green': 0,
'blue': 1,
'alpha': 1,
},
'firstBandColor': {
'red': 1,
'green': 0,
'blue': 0,
'alpha': 0,
},
'secondBandColor': {
'red': 0,
'green': 1,
'blue': 0,
'alpha': 0,
}
},
},
},
},

Categories

Resources