Updating/Appending to json (nested) in python - python

My JSON file is shown below
{
"PersonA": {
"Age": "35",
"Place": "Berlin",
"cars": ["Ford", "BMW", "Fiat"]
},
"PersonB": {
"Age": "45",
"Cars": ["Kia", "Ford"]
},
"PersonC": {
"Age": "55",
"Place": "London"
}
}
I'm trying to update certain entries on this json E.g. set Place for PersonB to Rome similarly for PersonC update cars with an array ["Hyundai", "Ford"]`
What I have done until now is
import json
key1 ='PersonB'
key2 = 'PersonC'
filePath = "resources/test.json"
with open(filePath, encoding='utf-8') as jsonFile:
jsonData = json.load(jsonFile)
print(jsonData)
PersonBUpdate = {"Place" : "Rome"}
PersonCUpdate = {"cars" : ["Hyundai", "Ford"]}
jsonData[key1].append(PersonBUpdate)
jsonData[key2].append(PersonCUpdate)
print(jsonData)
It throws an error.
AttributeError: 'dict' object has no attribute 'append'

It should be like this:
jsonData['Person1']['Place'] = 'Rome'
Dictionaries indeed do not have an append method. Only lists do.
Or with Python 3 you can do this:
jsonData['Person1'].update(PersonBUpdate)

list.append is a method for type list, not dict. Always make sure to look at the full method signature to see what type a method belongs to.
Instead we can use dict.update:
Update the dictionary with the key/value pairs from other, overwriting existing keys. Return None.
update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). If keyword arguments are specified, the dictionary is then updated with those key/value pairs: d.update(red=1, blue=2).
And use this method in your code like this:
jsonData[key1].update(PersonBUpdate)
jsonData[key2].update(PersonCUpdate)
Which gives the expected result:
{'PersonA': {'Age': '35', 'Place': 'Berlin', 'cars': ['Ford', 'BMW', 'Fiat']}, 'PersonB': {'Age': '45', 'Cars': ['Kia', 'Ford'], 'Place': 'Rome'}, 'PersonC': {'Age': '55', 'Place': 'London', 'cars': ['Hyundai', 'Ford']}}

Related

Referencing Values in a List (syntax issue?) [duplicate]

I wrote some code to get data from a web API. I was able to parse the JSON data from the API, but the result I gets looks quite complex. Here is one example:
>>> my_json
{'name': 'ns1:timeSeriesResponseType', 'declaredType': 'org.cuahsi.waterml.TimeSeriesResponseType', 'scope': 'javax.xml.bind.JAXBElement$GlobalScope', 'value': {'queryInfo': {'creationTime': 1349724919000, 'queryURL': 'http://waterservices.usgs.gov/nwis/iv/', 'criteria': {'locationParam': '[ALL:103232434]', 'variableParam': '[00060, 00065]'}, 'note': [{'value': '[ALL:103232434]', 'title': 'filter:sites'}, {'value': '[mode=LATEST, modifiedSince=null]', 'title': 'filter:timeRange'}, {'value': 'sdas01', 'title': 'server'}]}}, 'nil': False, 'globalScope': True, 'typeSubstituted': False}
Looking through this data, I can see the specific data I want: the 1349724919000 value that is labelled as 'creationTime'.
How can I write code that directly gets this value?
I don't need any searching logic to find this value. I can see what I need when I look at the response; I just need to know how to translate that into specific code to extract the specific value, in a hard-coded way. I read some tutorials, so I understand that I need to use [] to access elements of the nested lists and dictionaries; but I can't figure out exactly how it works for a complex case.
More generally, how can I figure out what the "path" is to the data, and write the code for it?
For reference, let's see what the original JSON would look like, with pretty formatting:
>>> print(json.dumps(my_json, indent=4))
{
"name": "ns1:timeSeriesResponseType",
"declaredType": "org.cuahsi.waterml.TimeSeriesResponseType",
"scope": "javax.xml.bind.JAXBElement$GlobalScope",
"value": {
"queryInfo": {
"creationTime": 1349724919000,
"queryURL": "http://waterservices.usgs.gov/nwis/iv/",
"criteria": {
"locationParam": "[ALL:103232434]",
"variableParam": "[00060, 00065]"
},
"note": [
{
"value": "[ALL:103232434]",
"title": "filter:sites"
},
{
"value": "[mode=LATEST, modifiedSince=null]",
"title": "filter:timeRange"
},
{
"value": "sdas01",
"title": "server"
}
]
}
},
"nil": false,
"globalScope": true,
"typeSubstituted": false
}
That lets us see the structure of the data more clearly.
In the specific case, first we want to look at the corresponding value under the 'value' key in our parsed data. That is another dict; we can access the value of its 'queryInfo' key in the same way, and similarly the 'creationTime' from there.
To get the desired value, we simply put those accesses one after another:
my_json['value']['queryInfo']['creationTime'] # 1349724919000
I just need to know how to translate that into specific code to extract the specific value, in a hard-coded way.
If you access the API again, the new data might not match the code's expectation. You may find it useful to add some error handling. For example, use .get() to access dictionaries in the data, rather than indexing:
name = my_json.get('name') # will return None if 'name' doesn't exist
Another way is to test for a key explicitly:
if 'name' in resp_dict:
name = resp_dict['name']
else:
pass
However, these approaches may fail if further accesses are required. A placeholder result of None isn't a dictionary or a list, so attempts to access it that way will fail again (with TypeError). Since "Simple is better than complex" and "it's easier to ask for forgiveness than permission", the straightforward solution is to use exception handling:
try:
creation_time = my_json['value']['queryInfo']['creationTime']
except (TypeError, KeyError):
print("could not read the creation time!")
# or substitute a placeholder, or raise a new exception, etc.
Here is an example of loading a single value from simple JSON data, and converting back and forth to JSON:
import json
# load the data into an element
data={"test1": "1", "test2": "2", "test3": "3"}
# dumps the json object into an element
json_str = json.dumps(data)
# load the json to a string
resp = json.loads(json_str)
# print the resp
print(resp)
# extract an element in the response
print(resp['test1'])
Try this.
Here, I fetch only statecode from the COVID API (a JSON array).
import requests
r = requests.get('https://api.covid19india.org/data.json')
x = r.json()['statewise']
for i in x:
print(i['statecode'])
Try this:
from functools import reduce
import re
def deep_get_imps(data, key: str):
split_keys = re.split("[\\[\\]]", key)
out_data = data
for split_key in split_keys:
if split_key == "":
return out_data
elif isinstance(out_data, dict):
out_data = out_data.get(split_key)
elif isinstance(out_data, list):
try:
sub = int(split_key)
except ValueError:
return None
else:
length = len(out_data)
out_data = out_data[sub] if -length <= sub < length else None
else:
return None
return out_data
def deep_get(dictionary, keys):
return reduce(deep_get_imps, keys.split("."), dictionary)
Then you can use it like below:
res = {
"status": 200,
"info": {
"name": "Test",
"date": "2021-06-12"
},
"result": [{
"name": "test1",
"value": 2.5
}, {
"name": "test2",
"value": 1.9
},{
"name": "test1",
"value": 3.1
}]
}
>>> deep_get(res, "info")
{'name': 'Test', 'date': '2021-06-12'}
>>> deep_get(res, "info.date")
'2021-06-12'
>>> deep_get(res, "result")
[{'name': 'test1', 'value': 2.5}, {'name': 'test2', 'value': 1.9}, {'name': 'test1', 'value': 3.1}]
>>> deep_get(res, "result[2]")
{'name': 'test1', 'value': 3.1}
>>> deep_get(res, "result[-1]")
{'name': 'test1', 'value': 3.1}
>>> deep_get(res, "result[2].name")
'test1'

Removing certain set of value within Python request text

I got this chunk from a response text after calling an API. How do I remove only the set with '"id": 23732 along with its other key:values ,' from the string?
{
"jobs": [
{
"id": 23732,
"status": "done",
"name": "TESTRBZ7664"
},
{
"id": 23730,
"status": "done",
"name": "RBY5434"
}
]
}
TQ
Convert the string to a json using json.loads() or response.json()
See the following code
In [4]: d
Out[4]:
{'jobs': [{'id': 23732, 'status': 'done', 'name': 'TESTRBZ7664'},
{'id': 23730, 'status': 'done', 'name': 'RBY5434'}]}
In [5]: [i for i in d["jobs"] if i["id"] != 23732]
Out[5]: [{'id': 23730, 'status': 'done', 'name': 'RBY5434'}]
Assuming the dict you posted is called original_dict, you could build a new dict using a list comprehension:
new_data = {
"jobs": [x for x in original_dict if x["id"] != 23732]
}
This doesn't strictly remove the entry from your original dict, it rather creates a new dict that doesn't contain the unwanted entry.
Read more about list comprehensions here: https://www.w3schools.com/python/python_lists_comprehension.asp

Create partial dict from recursively nested field list

After parsing a URL parameter for partial responses, e.g. ?fields=name,id,another(name,id),date, I'm getting back an arbitrarily nested list of strings, representing the individual keys of a nested JSON object:
['name', 'id', ['another', ['name', 'id']], 'date']
The goal is to map that parsed 'graph' of keys onto an original, larger dict and just retrieve a partial copy of it, e.g.:
input_dict = {
"name": "foobar",
"id": "1",
"another": {
"name": "spam",
"id": "42",
"but_wait": "there is more!"
},
"even_more": {
"nesting": {
"why": "not?"
}
},
"date": 1584567297
}
should simplyfy to:
output_dict = {
"name": "foobar",
"id": "1",
"another": {
"name": "spam",
"id": "42"
},
"date": 1584567297,
}
Sofar, I've glanced over nested defaultdicts, addict and glom, but the mappings they take as inputs are not compatible with my list (might have missed something, of course), and I end up with garbage.
How can I do this programmatically, and accounting for any nesting that might occur?
you can use:
def rec(d, f):
result = {}
for i in f:
if isinstance(i, list):
result[i[0]] = rec(d[i[0]], i[1])
else:
result[i] = d[i]
return result
f = ['name', 'id', ['another', ['name', 'id']], 'date']
rec(input_dict, f)
output:
{'name': 'foobar',
'id': '1',
'another': {'name': 'spam', 'id': '42'},
'date': 1584567297}
here the assumption is that on a nested list the first element is a valid key from the upper level and the second element contains valid keys from a nested dict which is the value for the first element

Add value for key in dictionary which is inside the list in python

I have to prepare JSON files to send using POST, but I have faced the following format to handle:
offer {
"location":
{
"city": "Kharkov",
"address": "street"
}
"dates": [
{
"start_date": "2018-10-10 14:00",
"end_date": "2018-11-11 14:00"
}]
}
Before it, to set the Value for the city field I used the following implementation:
offer['location']['city'] = "Kharkov"
But now I can't figure out how to add the value to the key start_date since the dictionary is inside the list.
'dates' is a list. Use index to access the key inside.
Ex:
offer['dates'][0]["start_date"] = "NewDate"
print(offer)
Output:
{'dates': [{'start_date': 'NewDate', 'end_date': '2018-11-11 14:00'}], 'location': {'city': 'Kharkov', 'address': 'street'}}

Python: tree like implementation of dict datastructure [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
I have dict object something similar to like this,
topo = {
'name' : 'm0',
'children' : [{
'name' : 'm1',
'children' : []
}, {
'name' : 'm2',
'children' : []
}, {
'name' : 'm3',
'children' : []
}]
}
Now, i want to insert one more dict object let's say,
{
'name' : 'ABC',
'children' : []
}
as child of dict named "m2" inside m2's children array.
Could you please suggest how should i go about it?
Should i go for a separate data structure implementation ?
I would suggest you first convert it to a data structure like this:
topo = {
'm0' : {
'm1' : {},
'm2' : {},
'm3' : {},
},
}
That is, you have made every value for the 'name' key be a key in a dictionary, and every value for the 'children' key be the value for that key, and changed it to a dictionary instead of a list.
Now you don't need to assume beforehand the index position where m2 is found. You do need to know that m2 is inside m0, but then you can simply say
topo['m0']['m2']['ABC'] = {}
You can convert between formats with this code:
def verbose_to_compact(verbose):
return { item['name']: verbose_to_compact(item['children']) for item in verbose }
def compact_to_verbose(compact):
return [{'name':key, 'children':compact_to_verbose(value)} for key, value in compact]
Call them like this
compact_topo = verbose_to_compact([topo]) # function expects list; make one-item list
verbose_topo = compact_to_verbose(compact_topo)[0] # function returns list; extract the single item
I am assuming the format you have is the direct interpretation of some file format. You can read it in that way, convert it, work with it in the compact format, and then just convert it back when you need to write it out to a file again.
Your issue is a common Tree structure, you can consider to use http://pythonhosted.org/ete2/tutorial/tutorial_trees.html and populate each node with your dict value (don't reinvent the wheel).
Add it in the dictionary as you normally would, with the use of .append():
topo['children'][1]['children'].append({'name' : 'ABC', 'children' : []})
topo is now:
{
"name": "m0",
"children": [
{
"name": "m1",
"children": []
},
{
"name": "m2",
"children": [
{
"name": "ABC",
"children": []
}
]
},
{
"name": "m3",
"children": []
}
]
}
topo['children'][1]['children'].append({'name': 'ABC', 'children': []})
This adds the new dictionary under the children of the second child of topo:
{'children': [{'children': [], 'name': 'm1'},
{'children': [{'children': [], 'name': 'ABC'}], 'name': 'm2'},
{'children': [], 'name': 'm2'}],
'name': 'm0'}
But I would not use dict and list builtin objects for such a task - I'd rather create my own objects.
topo['children'].append({'name' : 'ABC',
'children' : []
})

Categories

Resources