So I am trying to work with an api currently which returns json data such as:
{
"emp1" : {
"EmpName" : "Chris Jackman",
"EmpAge" : "34",
"EmpGender" : "Male",
"EmpDept" : "IT",
"EmpDesg" : "Project Manager"
"EmpSal": {
"2019": 20000
"2020": 23000
},
"emp2" : {
"EmpName" : "Mary Jane",
"EmpAge" : "27",
"EmpGender" : "Female",
"EmpDept" : "IT"
}
}
Occasionally when we query the API one of these fields will be missing. When we try to access it
my_var = my_json["emp2"]["EmpDesg"]
Which will return a key error:
data_dict["EmpDesg"] = my_json["emp2"]["EmpDesg"]
KeyError: 'EmpDesg'
Is there a way to neatly deal with json paths missing rather than wrapping each field in a try catch:
try:
data_dict["EmpName"] = my_json["emp1"]["EmpName"]
except KeyError as e:
data_dict["EmpName"] = ""
try:
data_dict["EmpAge"] = my_json["emp1"]["EmpAge"]
except KeyError as e:
data_dict["EmpAge"] = ""
try:
data_dict["EmpGender"] = my_json["emp1"]["EmpGender"]
except KeyError as e:
data_dict["EmpGender"] = ""
try:
data_dict["salary"] = my_json["emp1"]["EmpSal"]["2020"]
except KeyError as e:
data_dict["salary"] = ""
If you want to check if a key exists, use "key" in data, not a try, except KeyError
Otherwise, if all you want is a default when a key doesn't exist, use .get() with the default parameter
emp1 = my_json["emp1"]
data_dict = {
"EmpName": emp1.get("EmpName", '')
"EmpAge": emp1.get("EmpAge", '')
}
Or
keys = ['EmpName', 'EmpAge']
data_dict = {k: emp1.get(k, '') for k in keys}
To check if a path of arbitrary length exists in a given dict:
def path_exists(d, path) -> bool:
"""Return True if path exists in given dict."""
next = d
while path:
k = path.pop(0)
if k in next:
next = next[k]
else:
return False
return True
For example:
In [4]: path_exists(d, ['emp1', 'EmpName'])
Out[4]: True
In [5]: path_exists(d, ['emp1', 'EmpSal', '2019'])
Out[5]: True
In [6]: path_exists(d, ['emp1', 'EmpSal', '2018'])
Out[6]: False
So you can check whether the path exists (just once) before confidently traversing the dict.
Related
I'm parsing a json file that looks like:
my_json:
"DataChangedEntry": {
"CurrentValue": {
"RefId": {
"Value": "aaaaaaa"
So to get "Value" it looks like:
my_json["DataChangedEntry"]["CurrentValue"]["RefId"]["Value"]
I want to send it to a try/except function (because I have a lot of fields to get) but I don't know how to send the json object over.
I've tried:
get_value = my_function(my_json, ["DataChangedEntry"]["CurrentValue"]["RefId"]["Value"])
But I get error:
TypeError: list indices must be integers or slices, not str
The my_function is just
def my_function(json_prefix, json_field):
try:
value = json_prefix[json_field]
return value
except:
logging.exception('Exception: ')
You have to pass each key as a separate argument (or as a list of separate arguments).
def my_function(obj, *fields):
for f in fields:
try:
obj = obj[f]
except KeyError:
logging.exception("Exceptoin: ")
return
return obj
my_function(my_json, "DataChangedEntry", "CurrentValue", ...)
I confess that this idea of sending this data separately to a function is quite different.
But to directly return the value aaaaaaa:
Send the first argument as the JSON object value
Send the second argument as the JSON object name string
Send third argument as key list
Then you can use eval() to convert the union of strings into code:
def my_function(json_full, json_prefix, json_field):
my_json = json_full
my_json_str = json_prefix
key_field = '["' + '"]["'.join(json_field) + '"]'
try:
value = eval(f'{json_prefix}{key_field}')
return value
except Exception as e:
return e
def main():
my_json = {
"DataChangedEntry": {
"CurrentValue": {
"RefId": {
"Value": "aaaaaaa"
},
},
},
}
get_value = my_function(my_json, 'my_json', ["DataChangedEntry","CurrentValue","RefId","Value"])
print(get_value)
if __name__ == "__main__":
main()
Output:
aaaaaaa
I'm not able to update the testCase defined on Rally using pyral
Below is the code snippet I am using:
# Get the testcase that needs to be updated
query_criteria = 'FormattedID = "%s"' % tc
rally_response = rally_obj.get('TestCase', fetch=True, query=query_criteria)
target_project = rally.getProject()
testcase_fields = {
"Project" : target_project.ref,
"Workspace" : "workspace/59461540411",
"Name" : "test",
"Method" : "Automated",
"Type" : "Acceptance",
"Notes" : "netsim testing",
"Description" : "description changing",
#"TestCase" : "testcase/360978196712"
}
testcase_response = rally.put('TestCase', testcase_fields)
The status code of the testcase_response is "200" , but the Test Case is not updated.
What is wrong?
You are mixing 2 functions:
rally.put: to create a new WorkItem
rally.update: to modify an existing WorkItem
When you modify an item, you need the reference, FormattedID, or Object ID of the Workitem.
Your code should look like something like that:
# Get the testcase that needs to be updated
import logging
logging.basicConfig(format="%(levelname)s:%(module)s:%(lineno)d:%(msg)s")
def get_test_case(tcid):
"""Retrieve Test Case from its ID, or None if no such TestCase id"""
query_criteria = "FormattedID = %s" % tcid
try:
response = rally_obj.get('TestCase', fetch=True, query=query_criteria)
return response.next()
except StopIteration:
logging.error("Test Case '%s' not found" % tcid)
return None
target_project = rally.getProject()
test_case = get_test_case("TC1234")
if test_case:
testcase_fields = {
"FormattedID" : test_case.FormattedID
"Project" : target_project.ref,
"Workspace" : "workspace/59461540411", # might not be necessary if you don't change it
"Name" : "test",
"Method" : "Automated",
"Type" : "Acceptance",
"Notes" : "netsim testing",
"Description" : "description changed",
}
testcase_response = rally.update('TestCase', testcase_fields)
print(testcase_response)
NOTE: you don't have to have double quotes " in the query.
I have a JSON file where each object looks like the following example:
[
{
"timestamp": 1569177699,
"attachments": [
],
"data": [
{
"post": "\u00f0\u009f\u0096\u00a4\u00f0\u009f\u0092\u0099"
},
{
"update_timestamp": 1569177699
}
],
"title": "firstName LastName"
}
]
I want to check if, there is the key post, nested within the key data. I wrote this, but it doesn't work:
posts = json.loads(open(file).read())
for post in posts:
if 'data' in post:
if 'post' in post['data']
print post['data']['post']
Here is my solution. I see from your sample data that post["data"] is a list, so the program should iterate over it:
posts = json.loads(open(file).read())
for post in posts:
if 'data' in post:
#THIS IS THE NEW LINE to iterate list
for d in post["data"]:
if 'post' in d:
print d['post']
Try:
posts = json.loads(open(file).read())
for data in posts:
for key, value in data.items():
if key == 'data':
for item in value:
if 'post' in item:
print(key, item['post'])
Try this answer this works!
Elegant way to check if a nested key exists in a python dict
def keys_exists(element, *keys):
'''
Check if *keys (nested) exists in `element` (dict).
'''
if not isinstance(element, dict):
raise AttributeError('keys_exists() expects dict as first argument.')
if len(keys) == 0:
raise AttributeError('keys_exists() expects at least two arguments, one given.')
_element = element
for key in keys:
try:
_element = _element[key]
except KeyError:
return False
return True
You could do it generically by adapting my answer to the question How to find a particular json value by key?.
It's generic in the sense that it doesn't care much about the details of how the JSON data is structured, it just checks every dictionary it finds inside it.
import json
def find_values(id, json_file):
results = []
def _decode_dict(a_dict):
try:
results.append(a_dict[id])
except KeyError:
pass
return a_dict
json.load(json_file, object_hook=_decode_dict) # Return value ignored.
return len(results) > 0 # If there are any results, id was found.
with open('find_key_test.json', 'r') as json_file:
print(find_values('post', json_file)) # -> True
please try the following:
posts = json.loads(open(file).read())
for post in posts:
if 'data' in post:
for data in post['data']:
if 'post' in data:
print(data['post'])
Print statement not printing anything on console in python.
I am printing the key, value of the JSON file where the key starts with #id
found = False
for key, di in json.loads(json_data).iteritems():
for k, v in di.items():
if k.startswith('#id'):
found = True
print k, v
sys.stout.flush()
break
if found:
break
I think have done a mistake. Any help will be appreciated.
Given the sample data you provided in the comment section, you can try this:
import json
json_data = '{ "nvd": { "entry": [ { "#id": "CVE-2016-0001", "vuln:cve-id": "CVE-2016-0001", "vuln:published-datetime": "2017-05-11T10:29:55.767-04:00", "vuln:last-modified-datetime": "2017-05-11T10:29:55.767-04:00", "vuln:summary": "** REJECT ** DO NOT USE THIS CANDIDATE NUMBER. " }]}}'
wantedKey = "#id"
found = False
for key, di in json.loads(json_data).items():
if wantedKey in di['entry'][0]:
found = True
print(wantedKey + " : " + di['entry'][0][wantedKey])
break
Output:
#id : CVE-2016-0001
I have a flask application which is receiving a request from dataTables Editor. Upon receipt at the server, request.form looks like (e.g.)
ImmutableMultiDict([('data[59282][gender]', u'M'), ('data[59282][hometown]', u''),
('data[59282][disposition]', u''), ('data[59282][id]', u'59282'),
('data[59282][resultname]', u'Joe Doe'), ('data[59282][confirm]', 'true'),
('data[59282][age]', u'27'), ('data[59282][place]', u'3'), ('action', u'remove'),
('data[59282][runnerid]', u''), ('data[59282][time]', u'29:49'),
('data[59282][club]', u'')])
I am thinking to use something similar to this really ugly code to decode it. Is there a better way?
from collections import defaultdict
# request.form comes in multidict [('data[id][field]',value), ...]
# so we need to exec this string to turn into python data structure
data = defaultdict(lambda: {}) # default is empty dict
# need to define text for each field to be received in data[id][field]
age = 'age'
club = 'club'
confirm = 'confirm'
disposition = 'disposition'
gender = 'gender'
hometown = 'hometown'
id = 'id'
place = 'place'
resultname = 'resultname'
runnerid = 'runnerid'
time = 'time'
# fill in data[id][field] = value
for formkey in request.form.keys():
exec '{} = {}'.format(d,repr(request.form[formkey]))
This question has an accepted answer and is a bit old but since the DataTable module seems being pretty popular among jQuery community still, I believe this approach may be useful for someone else. I've just wrote a simple parsing function based on regular expression and dpath module, though it appears not to be quite reliable module. The snippet may be not very straightforward due to an exception-relied fragment, but it was only one way to prevent dpath from trying to resolve strings as integer indices I found.
import re, dpath.util
rxsKey = r'(?P<key>[^\W\[\]]+)'
rxsEntry = r'(?P<primaryKey>[^\W]+)(?P<secondaryKeys>(\[' \
+ rxsKey \
+ r'\])*)\W*'
rxKey = re.compile(rxsKey)
rxEntry = re.compile(rxsEntry)
def form2dict( frmDct ):
res = {}
for k, v in frmDct.iteritems():
m = rxEntry.match( k )
if not m: continue
mdct = m.groupdict()
if not 'secondaryKeys' in mdct.keys():
res[mdct['primaryKey']] = v
else:
fullPath = [mdct['primaryKey']]
for sk in re.finditer( rxKey, mdct['secondaryKeys'] ):
k = sk.groupdict()['key']
try:
dpath.util.get(res, fullPath)
except KeyError:
dpath.util.new(res, fullPath, [] if k.isdigit() else {})
fullPath.append(int(k) if k.isdigit() else k)
dpath.util.new(res, fullPath, v)
return res
The practical usage is based on native flask request.form.to_dict() method:
# ... somewhere in a view code
pars = form2dict(request.form.to_dict())
The output structure includes both, dictionary and lists, as one could expect. E.g.:
# A little test:
rs = jQDT_form2dict( {
'columns[2][search][regex]' : False,
'columns[2][search][value]' : None,
'columns[2][search][regex]' : False,
} )
generates:
{
"columns": [
null,
null,
{
"search": {
"regex": false,
"value": null
}
}
]
}
Update: to handle lists as dictionaries (in more efficient way) one may simplify this snippet with following block at else part of if clause:
# ...
else:
fullPathStr = mdct['primaryKey']
for sk in re.finditer( rxKey, mdct['secondaryKeys'] ):
fullPathStr += '/' + sk.groupdict()['key']
dpath.util.new(res, fullPathStr, v)
I decided on a way that is more secure than using exec:
from collections import defaultdict
def get_request_data(form):
'''
return dict list with data from request.form
:param form: MultiDict from `request.form`
:rtype: {id1: {field1:val1, ...}, ...} [fieldn and valn are strings]
'''
# request.form comes in multidict [('data[id][field]',value), ...]
# fill in id field automatically
data = defaultdict(lambda: {})
# fill in data[id][field] = value
for formkey in form.keys():
if formkey == 'action': continue
datapart,idpart,fieldpart = formkey.split('[')
if datapart != 'data': raise ParameterError, "invalid input in request: {}".format(formkey)
idvalue = int(idpart[0:-1])
fieldname = fieldpart[0:-1]
data[idvalue][fieldname] = form[formkey]
# return decoded result
return data