How to assert deeply in JSON with python - python

I am trigerring an API call, and the server response is in JSON format.
the response looks like this:
{
"status": 0,
"not_passed": 1,
"why": [
{
"code": 229,
"reason": "some reason",
}
]
}
I need to assert two thing.
Status and reason
fro status I am using:
r = requests.get_simple(url=Server.MY_SERVER, params=p)
data = json.loads(r.content)
assert data["status"] == 0
but it doesn't work for the 'reason', maybe because the 'reason' is deeper in the nested structure. How can I fix this one?

assert data['why'][0]['reason'] == 'something'
Of course this assumes that data['why'] exists, is a list, and contains a dict as its first element.

Related

AttributeError 'bytes' object has no attribute 'get'

I'm managing an api and I would like to understand why I have the error "AttributeError 'bytes' object has no attribute 'get'"
import requests
from requests.auth import HTTPBasicAuth
incidents = []
limit = 100
offset = 0
got_all_events = False
while not got_all_events:
alerts = requests.get(f'***', auth=HTTPBasicAuth('***', '***'))
print(alerts)
for alert in alerts:
incident = {
'name': alert.get('hostname'),
'rawJSON': json.dumps(alert),
}
incidents.append(incident)
if len(alerts) == limit:
offset += limit
else:
got_all_events = True
print(incident)
The error is regarding this code line
'name': alert.get('hostname'),
From my api my array is defined like that:
{
"totalSize": 0,
"data": [
{
"hostname": "string",
"domain": "string",
"tags": "string",
"os": {},
}
],
"size": 0
}
Your dictionary has the hostname key nested in a subdictionary as part of a list, so the get is unable to find it.
The only valid targets for the get on alert are totalsize, data, and size.
You could run a get on alert with key data to return a list, then run another get on position 0 of that list with key hostname to get what you want.
A more straightforward approach would just be:
'name': alert["data"][0]["hostname"]
alerts is a requests.Response object, not something decoded from JSON. You have to extract the body first. The iterator for a Response yields a sequence of bytes values, which is where the bytes value you think should be a dict comes from.
response = requests.get(f'***', auth=HTTPBasicAuth('***', '***'))
alerts = response.json()
for alert in alerts:
incident = {
'name': alert.get('hostname'),
'rawJSON': json.dumps(alert),
}
incidents.append(incident)
Thanks for your help.
Now it is working like that:
r=alerts.json()
for alert in r['data']:
incident = {
'hostName': alert["hostname"],
}
incidents.append(incident)
print(json.dumps(incidents, indent=1))

Array in a json

I am creating a python project who are working with an api, the api return an json like this:
"1": "2018-10-13T08:28:38.809469028Z",
"result": [
{
"id":3027531,
"created_at":"2018-10-13T08:20:38.809469028Z",
"date":"2018-10-13T08:19:38Z",
"text":"banana",
}
],
I can get 1, but i can't get text in result, can someone help me?
I tried:
response.json()['result']['text']
response.json()['result'].text
response.json().result[0].text
Try this
response_json = response.json()
response_json["result"][0]["text"]
The value for result is a list of dictionaries. We take the first item in that list, and then we ask for the value of text.
Be aware that this assumes that response_json["result"] has at least one item. If it is empty, you will get an IndexError. You should probably check the length of response_json["result"] before using it. Here is an example of how this could be done:
response_json = response.json()
if response_json["result"]:
response_json["result"][0]["text"]
else:
print("result is empty")
d = {"1": "2018-10-13T08:28:38.809469028Z",
"result": [
{
"id":3027531,
"created_at":"2018-10-13T08:20:38.809469028Z",
"date":"2018-10-13T08:19:38Z",
"text":"banana",
}
]}
d['result'][0]['id'] # 3027531
d['result'][0]['text'] # banana

Passing the "continue" content of the wikipedia API gives me a badcontinue error

I'm trying to build a loop that gives me all the pageids of a category. Thus, I'm trying to use the "continue" parameter as intended.
Here is the initialization with a first query that is working :
import requests
URL = "https://fr.wikipedia.org/w/api.php"
PARAMS_FRANCE = {
"action": "query",
"cmtitle": 'Catégorie:Portail:France/Articles liés',
"list": "categorymembers",
"cmlimit": 500,
"format": "json"
}
R = S.get(url=URL, params=PARAMS_FRANCE)
DATA = R.json()
PAGES_FRANCE = DATA['query']['categorymembers']
idx_continue = DATA['continue']
But then, when it enters the loop :
while('continue' in DATA):
PARAMS_FRANCE = {
"action": "query",
"cmtitle": 'Catégorie:Portail:France/Articles liés',
"list": "categorymembers",
"cmlimit": 500,
"continue": idx_continue,
"format": "json"
}
R = S.get(url=URL, params=PARAMS_FRANCE)
DATA = R.json()
PAGES_FRANCE = DATA['query']['categorymembers']
idx_continue = DATA['continue']
It returns me the following error when I print DATA:
{
"error":{
"code":"badcontinue",
"info":"Invalid continue param. You should pass the original value returned by the previous query.",
"*":"See https://fr.wikipedia.org/w/api.php for API usage. Subscribe to the mediawiki-api-announce mailing list at <https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce&gt; for notice of API deprecations and breaking changes."
},
"servedby":"mw1390"
}
Except I gave all the content of continue so what could be the problem ?
Thank you
The correct way to use the continuation parameters is to merge them with the original parameters (ie. PARAMS_FRANCE.update(DATA['continue'])).
I have the same problem when I was trying to fetch the backlinks. I changed a little bit of your code, the setting of the parameters, like this:
PARAMS_RUSSIA2 = {
"action": "query",
"bltitle": 'Russia',
"list": "backlinks",
"bllimit": 500,
"blcontinue": idx_continue['blcontinue'],
"continue":idx_continue['continue'],
"format": "json"
}
In this way, I can get the second 500 results:)

Comparing value in a JSON using Python

I receive a fairly uncomfortable JSON to work with, which looks as follows:
[
{
"attributes": [
{
"type": "COMMAND",
"name": "COMMAND",
"value": [
"buttonState"
]
},
{
"type": "std_msgs.msg.Bool",
"name": "buttonState",
"value": {
"data": false
}
}
],
"type": "sensor",
"id": "s_2"
}]
And I would like to compare a piece of data (more precisely - value of Button state) but I seem to fail. Tried following:
import requests
import json
yo = 1
switchPost = "http://192.168.0.104:7896/iot/d?k=123456789&i=san_1_switch&d=sw|{}"
robGet = "http://192.168.0.109:10100/robot/sen_2"
r = requests.get(robGet, headers={"content-type":"application/json"})
resp = json.loads(r.text)
for attrs in (resp['attributes']['value']):
if attrs['data'] == false:
yo = 100
break
g = requests.post(switchPost.format(yo), headers={"content-type":"text/plain"})
print(r.text)
Unfortunately, the error I receive is the following:
for attrs in (resp['attributes']['value']):
TypeError: list indices must be integers, not str
In your JSON, the fact that it is wrapped in [ then ] means it is a JSON array, but with just one element.
So, as your error message suggests, resp needs an integer as its index, for which element of the array you want. resp[0] then refers to
{
"attributes": [
{
"type": "COMMAND",
"name": "COMMAND",
"value": [
"buttonState"
]
},
{
"type": "std_msgs.msg.Bool",
"name": "buttonState",
"value": {
"data": false
}
}
],
"type": "sensor",
"id": "s_2"
}
(notice no [] now, so it's a JSON object)
Then you want resp[0]['attributes'] to refer to the single part of this object, 'attributes' which again refers to an array.
Therefore for attribute in resp[0]['attributes'] will allow you to loop through this array.
To get the boolean value you want, you'll then want to find which element of that array has 'name' of 'buttonState' and check the corresponding 'value'.
In all, you're probably looking for something like:
for attribute in resp[0]['attributes']:
if attribute['name'] == 'buttonState' and attribute['value']['data'] is False:
# Do your thing here
resp is a list so, to get first element, access it as resp[0]. Same with resp[0]['attributes']
So you can access it as follows
resp[0]['attributes'][0]['value']
You can restructure your for loop as follows
for d in resp[0]['attributes']:
if isinstance(d['value'], dict) and d['value'].get('data') == false:
yo = 100
break
The answer is in the error message I think:
TypeError: list indices must be integers, not str
The first entry in attributes has a value that is a list, so you can't get 'data' from that.
Since you have a mix of types, you might need to check if 'value' is a list or a dict.
Edit:
Jumped the gun here I think. #dennlinger gives an explanation to your error message. But you'll get it again once you're past that...

List indicies must be integers. not str JSON reponse

I am baffled and do not know how to solve this error. I'm trying to grab every name inside a JSON response list.
My code looks like this.
def extract_strucutres_ids(expected_structures):
response = requests.get(JIRA_REST + "/structures", verify=False)
response = response.json()
for structure in response['structures']:
print structure['name']
The Json reponse looks like this.
{
"structures": [{
"id": 165,
"name": "6.2 External Notifications Refactor",
"description": ""
}, {
"id": 364,
"name": "6.4 Day/Night Mode and Idle Scene Mode",
"description": "",
"readOnly": true
}, {
"id": 140,
"name": "ACC 5 Regression",
"description": ""
}
]
}
I keep getting List indicies must be integers, not str.
Python version 2.7.10
try this -
import json
def extract_strucutres_ids(expected_structures):
response = requests.get(JIRA_REST + "/structures", verify=False)
if response.status_code==200:
response_json = json.loads(response.text)
for structure in response_json['structures']:
print structure['name']
else:
print("Response is {}".format(response.status_code))
Let me know,if it worked!
Use json.loads()
response = requests.get(..)
response = json.loads(response.text) # response.text is a string
for structure in response['structures']:
# Do something

Categories