Python loop through nested JSON object without root element [duplicate] - python

This question already has answers here:
How can I parse (read) and use JSON?
(5 answers)
Closed 2 months ago.
I've already checked here and here.
I have the following json which has no root element and I want to loop through each item in objects and print the value for object_code:
{
"count": 3,
"objects": [
{
"object_code": "HN1059"
},
{
"object_code": "HN1060"
},
{
"object_code": "VO1013"
}
]
}
I tried:
json='{"count": 3,"objects": [{"object_code": "HN1059"},{"object_code": "HN1060"},{"object_code": "VO1013"}]}'
for obj in json['objects']:
print(obj.get('object_code'))
for obj in json[0]['objects']:
print(obj.get('object_code'))
Neither work and I get the error:
TypeError: string indices must be integers
UPDATE 1
The suggested solutions don't work for me, maybe that's because I'm using it in the context of a Scrapy class, here's the full code that throws error
TypeError: 'NoneType' object is not iterable
import json
import scrapy
class MySpider(scrapy.Spider):
name = 'mytest'
start_urls = []
def start_requests(self):
s='{"count": 3,"objects": [{"object_code": "HN1059"},{"object_code": "HN1060"},{"object_code": "VO1013"}]}'
obj = json.loads(s)
for o in obj['objects']:
print(o.get('object_code'))

You need to load fom the JSON content to python structure
import json
value = '{"count": 3,"objects": [{"object_code": "HN1059"},{"object_code": "HN1060"},{"object_code": "VO1013"}]}'
content = json.loads(value)
for obj in content['objects']:
print(obj.get('object_code'))

Use json.loads to convert the JSON string to a dict.
import json
s='{"count": 3,"objects": [{"object_code": "HN1059"},{"object_code": "HN1060"},{"object_code": "VO1013"}]}'
obj = json.loads(s)
for o in obj['objects']:
print(o.get('object_code'))

Related

Python + JSON Formatting ('list' object has no attribute 'values')

I'm getting the following error. I'm confident the error is due to my JSON formatting. How should I be formatting my JSON file?
Exception has occurred: AttributeError
'list' object has no attribute 'values'
The error is occurring on the following line
total_sites = len(custom_sites.values())
The function it's trying to execute
def get_available_websites():
string = []
with open('settings.json') as available_file:
for sites in json.load(available_file)['custom_sites']:
string.append(sites + ", ")
return (''.join(string))[:-2]
The JSON file
{
"custom_sites": [
"https://github.com",
"https://test.com"
]
}
I've tried various changes in the JSON file. Alternating [], and {}
custom_sites will be a list not a dict so it wont have a values attribute. you can just check the length of the list its self.
import json
json_str = """{
"custom_sites": [
"https://github.com",
"https://test.com"
]
}"""
custom_sites = json.loads(json_str)["custom_sites"]
total_sites = len(custom_sites)
print(f"{total_sites=}")
OUTPUT
total_sites=2

How to handle missing JSON nested keys from an API response in python?

Here is the JSON response I get from an API request:
{
"associates": [
{
"name":"DOE",
"fname":"John",
"direct_shares":50,
"direct_shares_details":{
"shares_PP":25,
"shares_NP":25
},
"indirect_shares":50,
"indirect_shares_details": {
"first_type": {
"shares_PP": 25,
"shares_NP": 0
},
"second_type": {
"shares_PP": 25,
"shares_NP": 0
}
}
}
]
}
However, in some occasions, some values will be equal to None. In that case, I handle it in my function for all the values that I know will be integers. But it doesn't work in this scenario for the nested keys inside indirect_shares_details:
{
"associates": [
{
"name":"DOE",
"fname":"John",
"direct_shares":50,
"direct_shares_details":{
"shares_PP":25,
"shares_NP":25
},
"indirect_shares":None,
"indirect_shares_details": None
}
}
]
}
So when I run my function to get the API values and put them in a custom dict, I get an error because the keys are simply inexistant in the response.
def get_shares_data(response):
associate_from_api = []
for i in response["associates"]:
associate_data = {
"PM_shares": round(company["Shares"], 2),
"full_name": i["name"] + " " + ["fname"]
"details": {
"shares_in_PM": i["direct_shares"],
"shares_PP_in_PM": i["direct_shares_details"]["shares_PP"],
"shares_NP_in_PM": i["direct_shares_details"]["shares_NP"],
"shares_directe": i["indirect_shares"],
"shares_indir_PP_1": i["indirect_shares_details"]["first_type"]["shares_PP"],
"shares_indir_NP_1": i["indirect_shares_details"]["first_type"]["shares_NP"],
"shares_indir_PP_2": i["indirect_shares_details"]["second_type"]["shares_PP"],
"shares_indir_NP_2": i["indirect_shares_details"]["second_type"]["shares_NP"],
}
}
for key,value in associate_data["details"].items():
if value != None:
associate_data["details"][key] = value * associate_data["PM_shares"] / 100
else:
associate_data["calculs"][key] = 0.0
associate_from_api.append(associate_data)
return associate_from_api
I've tried conditioning the access of the nested keys only if the parent key wasn't equal to None but I ended up declaring 3 different dictionaries inside if/else conditions and it turned into a mess, is there an efficient way to achieve this?
You can try accessing the values using dict.get('key') instead of accessing them directly, as in dict['key'].
Using the first approach, you will get None instead of KeyError if the key is not there.
EDIT: tested using the dictionary from the question:
You can try pydantic
Install pydantic
pip install pydantic
# OR
conda install pydantic -c conda-forge
Define some models based on your response structure
from pydantic import BaseModel
from typing import List, Optional
# There are some common fields in your json response.
# So you can put them together.
class ShareDetail(BaseModel):
shares_PP: int
shares_NP: int
class IndirectSharesDetails(BaseModel):
first_type: ShareDetail
second_type: ShareDetail
class Associate(BaseModel):
name: str
fname: str
direct_shares: int
direct_shares_details: ShareDetail
indirect_shares: int = 0 # Sets a default value for this field.
indirect_shares_details: Optional[IndirectSharesDetails] = None
class ResponseModel(BaseModel):
associates: List[Associate]
use ResponseModel.parse_xxx functions to parse response.
Here I use parse_file funtion, you can also use parse_json function
See: https://pydantic-docs.helpmanual.io/usage/models/#helper-functions
def main():
res = ResponseModel.parse_file("./NullResponse.json",
content_type="application/json")
print(res.dict())
if __name__ == "__main__":
main()
Then the response can be successfully parsed. And it automatically validates the input.

How to get details of a JSON Server response parsed into list/dictionary in Python

I am new to Python. I have been trying to parse the response sent as parameter in a function.
I have been trying to convert a function from Perl to Python.
The Perl block looks something like this:
sub fetchId_byusername
{
my ($self,$resString,$name) =#_;
my $my_id;
my #arr = #{$json->allow_nonref->utf8->decode($resString)};
foreach(#arr)
{
my %hash = %{$_};
foreach my $keys (keys %hash)
{
$my_id = $hash{id} if($hash{name} eq $name);
}
}
print "Fetched Id is : $my_id\n";
return $my_id;
The part where JSON data is being parsed is troubling me. How do i write this in python3.
I tried something like
def fetchID_byUsername(self, resString, name):
arr = []
user_id = 0
arr = resString.content.decode('utf-8', errors="replace")
for item in arr:
temp_hash = {}
temp_hash = item
for index in temp_hash.keys():
if temp_hash[name] == name:
user_id = temp_hash[id]
print("Fetched ID is: {}".format(user_id))
return user_id
Now I am not sure, if this is the right way to do it.
The json inputs are something like:
[{"id":12345,"name":"11","email":"11#test.com","groups":[{"id":6967,"name":"Test1"},{"id":123456,"name":"E1"}],"department":{"id":3863,"name":"Department1"},"comments":"111","adminUser":false},{"id":123457,"name":"1234567","email":"1234567#test.com","groups":[{"id":1657,"name":"mytest"},{"id":58881,"name":"Service Admin"}],"department":{"id":182,"name":"Service Admin"},"comments":"12345000","adminUser":true}]
Thanks in advance.
Your json input should be valid python I changed false to False and true to True. If it is json formatted string you can do
import json
data=json.loads(json_formatted_string_here) #data will be python dictionary herer
And tried like this it just iterates and when match found returns id
data=[{"id":12345,"name":"11","email":"11#test.com","groups":[{"id":6967,"name":"Test1"},{"id":123456,"name":"E1"}],"department":{"id":3863,"name":"Department1"},"comments":"111","adminUser":False},{"id":123457,"name":"1234567","email":"1234567#test.com","groups":[{"id":1657,"name":"mytest"},{"id":58881,"name":"Service Admin"}],"department":{"id":182,"name":"Service Admin"},"comments":"12345000","adminUser":True}]
def fetch_id_by_name(list_records,name):
for record in list_records:
if record["name"] == name:
return record["id"]
print(fetch_id_by_name(data,"11"))
First of all import the the json library and use json.loads() like:
import json
x = json.loads(json_feed) #This converts the json feed to a python dictionary
print(x["key"]) #values to "key"

Syntax to load nested in nested keys of JSON files

I have a big tree in a JSON file and I'm searching the python syntax for loading nested in nested keys from this JSON.
Assume I have this :
{
"FireWall": {
"eth0": {
"INPUT": {
"PING": 1,
}
}
}
}
According to the man page and some questions in Stackoverflow i tried this (and some variations) :
import json
config = open('config.json', 'r')
data = json.load('config')
config.close()
if data['{"FireWall", {"eth0", {"INPUT", {"Ping"}}}}'] == 1:
print('This is working')
With no result. What is the right way to do this (as simple as possible) ? Thank you !
You are trying data = json.load('config') to load string not file object and data['{"FireWall", {"eth0", {"INPUT", {"Ping"}}}}'] it's not right way to access nested dictionary key value.
import json
with open('config.json', 'r') as f:
data = json.load(f)
if data["FireWall"]["eth0"]["INPUT"]["Ping"] == 1:
print('This is working')
data is a nested dictionary, so:
data["FireWall"]["eth0"]["INPUT"]["Ping"]
will be equal to 1; or at least it will when you fix your call to json.load.
Try this:
data["FireWall"]["eth0"]["INPUT"]["PING"]
This will give you the value in PING

Parsing JSON to CSV with Python - AttributeError: 'str' object has no attribute 'keys'

This JSON to CSV code example is working great:
employee_data = '{"info":[{"employee_name": "James", "email": "james#gmail.com", "job_profile": "Sr. Developer"},{"employee_name": "Smith", "email": "Smith#gmail.com", "job_profile": "Project Lead"}]}'
#This can be parsed and converted to CSV using python as shown below:
import json
import csv
employee_parsed = json.loads(employee_data)
emp_data = employee_parsed['info']
# open a file for writing
employ_data = open('Employee.csv', 'w')
# create the csv writer object
csvwriter = csv.writer(employ_data)
count = 0
for emp in emp_data:
if count == 0:
header = emp.keys()
csvwriter.writerow([header])
count += 1
csvwriter.writerow([emp.values()])
employ_data.close()
My trouble comes in when I'm trying to use the following JSON data below...
I get an AttributeError: 'str' object has no attribute 'keys'. Please keep your response simple as this is my Python "Hello World". :-)
employee_data = '{"info": {"arch": "x86_64","platform": "linux"},"packages": {"_license-1.1-py27_0.tar.bz2": {"build": "py27_0","build_number": 0,"date": "2013-03-01","depends": ["python 2.7*"],"license": "proprietary - Continuum Analytics, Inc.","license_family": "Proprietary","md5": "5b13c8cd498ce15b76371ed85278e3a4","name": "_license","requires": [],"size": 194947,"version": "1.1"}}}'
Thank you for any direction on this.
The problem is that your code expects the JSON keys to be arrays. This loop here:
for emp in emp_data:
Expects each top level JSON key to be iterable (loopable). On your original JSON, the key info maps to a list:
[{"employee_name": "James", "email (...)
However, the ìnfo` key on the second JSON example does not map to a list but instead to a dictionary:
"info": {"arch": "x86_64","platform": "linux"}
Changing the info key to a list fixes it:
"info": [{"arch": "x86_64","platform": "linux"}]
In further depth, your emp_data variable looks like this:
{'platform': 'linux', 'arch': 'x86_64'}
And so when you iterate it (for emp in emp_data), emp will be "arch" or "platform", which you cannot get .keys() from.

Categories