Python - JSON Module how to query nested objects - python

I am trying to build a database-backed system using Python as an engine and JSON as the DB file. Currently, there are three files:
# functions.py
import json
from pprint import pprint
with open('db.json') as data_file:
data = json.load(data_file)
def getStudentByUsername(username, record):
detail = json.load(open('db.json'))["students"][username][record]
pprint(detail)
# main.py
import functions
functions.getStudentByUsername('youngh', 'age')
{ // db.json
"students": {
"youngh": {
"name": "Hayden Young",
"age": 13,
"email": "user#school.county.sch.uk"
}
}
}
But, I can't find a way to change my students object like this and still query by username:
{ //db.json "students": [
{
"username": "youngh",
"name": "Hayden Young"
}]
}
Any ideas? I'm new to this type of use of Python 3.

In your second example, you have the user attributes in a dictionary inside a list.
You will need to iterate over your list and get the dictionary with the username you seek.
with open('db.json') as dbfile:
for student in json.load(dbfile)["students"]:
if student["username"] == username:
return(student)
Keep in mind that because the usernames are contained in their own separate dictionaries, you can now have duplicate usernames, and the for loop method will only return the first match.

I think you can use lambda:
#let this var be your file output after parsing to json
users = {
"students":
[
{
"username": "youngh",
"name": "Hayden Young"
}
]
}
def getStudentByUsername(username, record):
detail= filter(lambda user: username == user['username'], users["students"])
print (next(detail)[record])
For duplicate usernames, you can do something like this:
def getStudentByUsername(username, record):
detail= filter(lambda user: username.lower() == user['username'].lower(), users["students"])
while True:
try:
print (next(detail)[record])
except StopIteration:
return

Related

Remove fields from a json array in Python

Currently I have a function returning json via jsonify.
[
{
"hostname": "bla",
"ipaddress": "192.168.1.10",
"subnetmask": "255.255.255.0",
"iloip": "192.168.1.11"
}
]
I want to keep it in json format, but I want to only show the fields I choose (i.e. reduce it). For this example, I want hostname and ipaddress.
Thanks
You can use dict comprehension:
json_input = '''
[
{
"hostname": "bla",
"ipaddress": "192.168.1.10",
"subnetmask": "255.255.255.0",
"iloip": "192.168.1.11"
}
]
'''
desired_keys = {'hostname', 'ipaddress'}
json_filtered = json.dumps([{ k:v for (k,v) in d.items() if k in desired_keys}
for d in json.loads(json_input)])
print(json_filtered)
output:
'[{"hostname": "bla", "ipaddress": "192.168.1.10"}]'
I belive what you want to achieve can be done with the code given below:
import json
data_json = '{"hostname": "bla","ipaddress": "192.168.1.10","subnetmask": "255.255.255.0","iloip": "192.168.1.11"}'
data = json.loads(data_json)
chosen_fields = ['hostname', 'ipaddress']
for field in chosen_fields:
print(f'{field}: {data[field]}')
Output:
hostname: bla
ipaddress: 192.168.1.10
Here what we do is we parse the stringified version of the json using the python's json module (i.e. json.loads(...)). Next decide on the fields we want to access (i.e. chosen_fields). Finally we iterate through the field we want to reach and get the corresponding values of the fields. This leaves the original json unmodified as you wished. Hope this helps.
Or else if you want these fields as a reduced json object:
import json
data_json = '{"hostname": "bla","ipaddress": "192.168.1.10","subnetmask": "255.255.255.0","iloip": "192.168.1.11"}'
data = json.loads(data_json)
chosen_fields = ['hostname', 'ipaddress']
reduced_json = "{"
for field in chosen_fields:
reduced_json += f'"{field}": "{data[field]}", '
reduced_json = list(reduced_json)
reduced_json[-2] = "}"
reduced_json = "".join(reduced_json)
reduced = json.loads(reduced_json)
for field in chosen_fields:
print(f'"{field}": "{reduced[field]}"')
Output:
"hostname": "bla"
"ipaddress": "192.168.1.10"
If I understand you correctly:
import json
response = [
{
"hostname": "bla",
"ipaddress": "192.168.1.10",
"subnetmask": "255.255.255.0",
"iloip": "192.168.1.11",
}
]
desired_keys = ["hostname", "ipaddress"]
new_response = json.dumps(
[{key: x[key] for key in desired_keys} for x in response]
)
And now you have a new_response - valid JSON, with which you can continue to work on

How to read data from JSON file in ddt Python

I am using ddt with python.
I have code to read csv which looks like this -
import csv
def getcsvdata(filename):
rows = []
datafile = open(filename, "r")
reader = csv.reader(datafile)
next(reader)
for row in reader:
rows.append(row)
return rows
How can I skip rows from a 'specified number of row' to 'specified number of row'?
In above code next (reader) is skipping header row.
Along-with I need to know how to read data from a JSON file?
example JSON file-
{
{
"email": "amit#some.com",
"passowrd": "123#123"
},
{
"email": "tanvi#some.com",
"passowrd": "123#456"
},
{
"email": "tc.u#some.io",
"passowrd": "123#789"
}
}
The only thing you need is the library json.
Python usually gets shipped with this lib included.
import json
def getJsonData(filepath):
return json.load(open(filepath))
data = getJsonData("the/file/path.json")
for item in data:
print(f"email -> {item['email']}")
print(f"password -> {item['password']}")
# output:
# email -> amit#some.com
# password -> 123#123
# email -> tanvi#some.com
# password -> 123#456
# email -> tc.u#some.io
# password -> 123#789
This is what I have done using json with ddt.
[
{
"email": "amit#some.com",
"passowrd": "123#123"
},
{
"email": "tanvi#some.com",
"passowrd": "123#456"
},
{
"email": "tc.u#some.io",
"passowrd": "123#789"
}
]
Then write your tests more like this.
import unittest
from ddt import ddt, data, unpack, file_data
#ddt
class TestCase(unittest.TestCase):
"""Some test case """
#file_data('test.json')
def test_email_and_username(self, email, password):
"""This tests some stuff"""
do some assertions here
Need to use json.
import json
json.loads(file object)

Compare expected vs actual json response for a get request in Python

You will get a small json response back when you go to this site https://reqres.in/api/users/2
I am saving the response in a variable(actual). I have also put the response in another variable(expected).Both responses are same. I am changing the values to test failed cases. The ultimate goal is to compare 2 and make sure they match.
I have 2 functions, 1 compares keys and value of both dictionaries and the other function sorts the dictionaries. Code below:
import json
import requests
response = requests.get('https://reqres.in/api/users/2')
#actual_response saves the json as we get it from url above
actual_response= json.loads(response.text)
#expected response is saved after using pretty json that will be used to testing/comparing actual vs expected
expected_response={
"data": {
"id": 2,
"first_name": "Janet",
"last_name": "Weaver",
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
}
}
# sort the key values before comparing
def dict_sort(dictA,dictB):
dictA, dictB = json.dumps(dictA, sort_keys=True), json.dumps(dictB, sort_keys=True)
dictA == dictB
#if there are any failure due to mismatch in key value the function below will show that
def key_diff(dictA,dictB):
for key,value in dictA.items():
for keyB,valueB in dictB.items():
for k,v in value.items():
for k2,v2 in valueB.items():
if(key!= keyB):
print('Expected',key,' but got',keyB)
if(k!=k2):
print('Expected', k, ' but got', k2)
if(v!=v2):
print('Expected', v, ' but got', v2)
else:
print()
dict_sort(actual_response,expected_response)
if(actual_response==expected_response):
print('Passed')
else:
print('Failed')
key_diff(actual_response,expected_response)
Problem: The test passes when there is no difference.However if there is any difference the order goes crazy. Here is an example where I changed data to dat inside expected response:
Expected data but got dat
Expected id but got last_name
Expected 2 but got Weaver
Should the sort function be more specific rather than using sort_keys=True?By the way thought about **args but I don't think that is a good choice in this scenario.
Thank You for your expert comment and time.
I advise to use unittest and avoid using so much nested for loops
from unittest import TestCase
import pandas as pd
import requests
def mocked_server_response():
expected = {"data": {"id": 2, "first_name": "Janet", "last_name": "Weaver",
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"}}
data = expected['data']
df = pd.DataFrame(my_dict['data'], index=[0])
return [expected, data, df]
At this point, the mocked_server_response()will get you this :
Out[27]:
id first_name last_name avatar
0 2 Janet Weaver https://s3.amazonaws.com/uifaces/faces/twitter...
Now, you can easily make test in a class.
class TestServerResponse(TestCase):
real_response = requests.get('https://reqres.in/api/users/2')
def setUp(self):
self.actual_response = real_response
def response(self):
self.assertEqual(self.actual_response, mocked_server_response()[0])
def test_data_in_response(self):
self.assertEqual(self.actual_response['data'], mocked_server_response()[1])
def test_dataframe(self):
self.assertEqual(pd.DataFrame(self.actual_response['data'], index=[0]), mocked_server_response()[2])
Key order is not guaranteed in Python versions under 3.7; you should use collections.OrderedDict when you need to create an object that remembers key order.
In Python 3.7 the insertion ordered is preserved, so your keys will always match.
import requests
import json
# Here I am converting the expected payload in dictionary
expected_payload = json.loads("""[
{
"key": "data-center",
"value": "All",
"values": [
"1",
"2"
]
},
{
"key": "router",
"value": "All",
"values": [
"cisco",
"juniper"
]
},
{
"key": "virtual-machine",
"value": "All",
"values": [
"dell",
"hp",
"None"
]
},
]""")
def test_get_all_system_attributes():
url = "http://" + str(ipaddr) + ":" + str(port) + "/Service/" + "system_attribute/"
payload = {}
headers = {
'Content-Type': 'application/json'
}
actual_response = requests.request("GET", url, headers=headers, data=payload)
assert json.loads(actual_response.text) == expected_payload
# json.loads(actual_response.text) will convert the response in dictionary
# using assert I am comparing the actual_response with exepcted_response
if __name__ == '__main__':
test_get_all_system_attributes()

convert list of class objects into json in python or django

my_objects = []
my_objects.append(Needed("bye",9))
my_objects.append(Needed("tata",8))
my_objects.append(Needed("hi",10))
i have list of object(example 5 objects in list) like this
class Needed:
def __init__(self, name, number):
self.name = name
self.number = number
and i need to convert this into json order by count like below
{
"results":[
{ "name":"hi",
"number":"10"
},
{ "name":"bye",
"number":"9"
},
{ "name":"tata",
"number":"8"
},
...........
...........
]
}
so how to achieve this in django
First you need to make your objects json-serializable. You could provide a default encoder function or write your own JSONEncoder as mentionned in the FineManual, but for a simple one-off case like your example building dicts from your objects is probably the simplest solution:
class Needed(object):
def __init__(self, name, number):
self.name = name
self.number = number
def to_dict(self):
return {"name": self.name, "number": self.number}
Then build a list of dicts from your objects:
results = [obj.to_dict() for obj in my_objects]
sort it based on number:
results.sort(key=lambda obj: obj["number"])
and serialize the whole thing:
jsdata = json.dumps({"results": results})
This is pretty ordinary Python stuff - building dictionaries out of objects, making a list of them and sorting the list.
Beside the fact that this is not django related, here is some Python code that can help you:
from operator import attrgetter
# sort objects by attribute number in reverse order
result = {'results': [obj.__dict__ for obj in sorted(my_objects, key=attrgetter('number'), reverse=True)]}
# `indent=4` returns a json string "prettyfied"
print(json.dumps(result, indent=4))
Which prints:
{
"results": [
{
"name": "hi",
"number": 10
},
{
"name": "bye",
"number": 9
},
{
"name": "tata",
"number": 8
}
...
]
}
Hope it helps.

$eq invalid syntax Mongo DB

I have a nested json and i want to find a record whose value is equal to a given number. I'm using pymongo in python wih equal operator but getting some errors:
from pymongo import MongoClient
import datetime
import json
import pprint
def connectDatabase(service):
try:
if service=="mongodb":
host = 'mongodb://connecion_string.mlab.com:31989'
database = 'xxx'
user = 'xxx'
password = 'xxx'
client = MongoClient(host)
client.xxx.authenticate(user, password, mechanism='SCRAM-SHA-1')
db = client.xxx
new_posts = [{"author": "Mike", "text": "Another post!",
"tags": [
{
"CSPAccountNo": "414693"
},
{
"CSPAccountNo": "349903"
}]
}]
result=db.posts1.insert_many(new_posts)
print (xxx.posts1.find( {"CSPAccountNo": { $eq: "414693" } } )
except Exception as e:
print(e)
pass
print (connectDatabase("mongodb"))
Error:
File "mongo.py", line 35
print (cstore.posts1.find( {"CSPAccountNo": { $eq: "414693" } } )
^
SyntaxError: invalid syntax
I'm very new to Mongodb. Any help would be appreciated
In Python you have to use Python syntax, not JS syntax like in the Mongo shell. That means that dictionary keys need quotes:
print(cstore.posts1.find( {"CSPAccountNo": { "$eq": "414693" } } )
(Note, in your code you never actually insert the new_posts into the collection, so your find call might not actually find anything.)
It should be
print (xxx.posts1.find( {"tags.CSPAccountNo": { $eq: "414693" } } )

Categories