Deserialize the JSON string into object in Python - python

I wrote this code to save and load an object into JSON.
I faced to this error when I accessed into attribute:
I use python json class to serialize the object and deserialize it.
AttributeError: 'dict' object has no attribute 'weeknumber'
# -*- coding: utf-8 -*-
import pickle
import pprint
from datetime import date
import datetime
import decimal
import json
class Jsonable(object):
def __iter__(self):
for attr, value in self.__dict__.iteritems():
if isinstance(value, datetime.datetime):
iso = value.isoformat()
yield attr, iso
elif isinstance(value, decimal.Decimal):
yield attr, str(value)
elif(hasattr(value, '__iter__')):
if(hasattr(value, 'pop')):
a = []
for subval in value:
if(hasattr(subval, '__iter__')):
a.append(dict(subval))
else:
a.append(subval)
yield attr, a
else:
yield attr, dict(value)
else:
yield attr, value
class MyCalendar(Jsonable):
year = 0
weeks = {}
def __init__(self, _year,_weeks):
self.year = _year
self.weeks = _weeks;
class MyWeek(Jsonable):
weeknumber = 0
days = {}
def __init__(self, _week,_days):
self.weeknumber = _week
self.days = _days
class MyDay(Jsonable):
daynumber = 0
courses = {}
def __init__(self, _day,_courses):
self.daynumber = _day
self.courses = _courses
class MyCourse(Jsonable):
id = 0
title = ''
def __init__(self, _id,_title):
self.id = _id
self.title = _title
def init():
_myc = MyCalendar(date.today().year,[]);
for wi in range(0, 52):
_week = MyWeek(wi,[])
for di in range(0, 5):
_day = MyDay(di,[])
for ci in range(0, 4):
_course = MyCourse(ci,"empty")
_day.courses.append(_course)
_week.days.append(_day)
_myc.weeks.append(_week)
return _myc
def save(_t):
with open('data2.txt', 'w') as outfile:
json.dump(dict(_t), outfile)
def load():
_t = init();
with open('data2.txt', 'r') as infile:
_t.__dict__ = json.loads(infile.read())
return _t
def setCourse(_t,_weeknumnber,_dayumber,_course,_title):
_t.weeks[_weeknumnber].days[_dayumber].courses[_course].title = _title
return _t
_mycal = init();
setCourse(_mycal,1,2,3,"Python");
save(_mycal)
_mycal = load()
pprint.pprint( _mycal.weeks[1].weeknumber)

Your code is essentially correct, but you're mixing a lot of aspects and parsing the JSON in an ugly way that gives you the current results where you can't access the attributes directly but you treat the objects created as a dictionary and need to access it by key. To achieve your way, and keep the attributes in tact you'll need to create your own parser for the classes. I've made a quick copy of your code on my workspace:
import json
class MyCalendar:
def __init__(self, _year, _weeks):
self.year = _year
self.weeks = _weeks
def to_JSON(self):
# Dumps the JSON correctly, passing over the __dict__ of the class to assmble it.
return json.dumps(self, default=lambda o: o.__dict__)
class MyWeek:
def __init__(self, _week, _days):
self.weeknumber = _week
self.days = _days
class MyDay:
def __init__(self, _day, _courses):
self.daynumber = _day
self.courses = _courses
class MyCourse:
def __init__(self, _id, _title):
self.id = _id
self.title = _title
def json_calendar(dct):
year = dct['year'] # year, for quick reference.
weeks = {} # our weeks, as a dict.
for week in dct['weeks']: # iterate through the available weeks in the given dct.
days = {} # our days, as a dict.
for day in dct['weeks'][week]['days']: # iterate through the available days in the week.
courses = {} # our courses, as a dict.
for course in dct['weeks'][week]['days'][day]['courses']: # iterate through the available courses in the day.
courseId = int(course) # course id
courseTitle = dct['weeks'][week]['days'][day]['courses'][course]['title'] # course title
courses[courseId] = MyCourse(courseId, courseTitle) # Assemble our courses into the courses dict.
days[int(day)] = MyDay(day, courses) # Assemble our days into the day dict.
weeks[int(week)] = MyWeek(week, days) # Assemble our weeks into the weeks dict.
return MyCalendar(year, weeks) # Assemble and return our calendar.
Now you can simply create your calendar, and now when you're going to dump it you just need to do _mycal.To_JSON() and to get the calendar object from the JSON you need to do _mycal = json_calendar(json_data) and you can access the attributes without any issues since we parsed it in our specific parser.

Related

how to create a python object from a json paylaod, without declaring each variable manually

basically I've got a booking class with fields each declared manually. I am curious if theres a more neat solution for the given task.
class Booking:
def __init__(self, json_data):
self.referenceNumber = json_data["referenceNumber"]
self.fromDate = json_data["fromDate"]
self.preferredDate1 = json_data["preferredDate1"]
self.preferredTimeFrom = json_data["preferredTimeFrom"]
self.time = json_data["time"]
self.partySize = json_data["partySize"]
self.budgetAmountTotal = json_data["budgetAmountTotal"]
self.budgetAmountPerPerson = json_data["budgetAmountPerPerson"]
self.budgetCurrencySign = json_data["budgetCurrencySign"]
self.venueName = json_data["venueName"]
self.venueId = json_data["venueId"]
self.cityName = json_data["cityName"]
self.clientName = json_data["clientName"]
self.clientContactName = json_data["clientContactName"]
self.status = json_data["status"]
self.statusText = json_data["statusText"]
self.assigneeId = json_data["assigneeId"]
self.assignee = json_data["assignee"]
self.lastAction = json_data["lastAction"]
self.inquiryChannel = json_data["inquiryChannel"]
self.venueDateFormat = json_data["venueDateFormat"]
self.bookingId = json_data["bookingId"]
self.inquiryHold = json_data["inquiryHold"]
self.isSpaceSelectedForHolds = json_data["isSpaceSelectedForHolds"]
self.id = json_data["id"]
bonus if i am not given an unresolved reference warning when I am calling for the attribute.
A simple self.__dict__.update(json_data) might do the trick.
Given the code in the question, access to attributes would be, for example, in the style of:
Booking().fromDate
However, if you don't mind accessing the value by its key name then you can greatly simplify the code as follows:
class Booking:
def __init__(self, json_data):
self._json = json.data
def __getitem__(self, key):
return self._json.get(key)
Then (for example)...
pd = {'fromDate': '2022/10/18'}
print(Booking(pd)['fromDate'])
How about this?
def set_attr_from_json(obj, json_data):
for key, value in json_data.items():
setattr(obj, key, value)
class Booking:
def __init__(self, json_data):
set_attr_from_json(self, json_data)
Since you want to control how your class attributes should be built, I think you should use metaclass like this
import json
from types import SimpleNamespace
class BookingMeta(type):
def __call__(self, json_data):
obj = super().__call__(json_data)
obj = json.loads(json.dumps(json_data), object_hook=lambda d: SimpleNamespace(**d))
return obj
class Booking(metaclass=BookingMeta):
def __init__(self, json_data):
pass
b = Booking({"name": {"first_name": "hello", "last_name": "world"}, "age": 23, "sub": ["maths", "Science"]})
print(b.name.first_name)
print(b.age)
#hello
#23

Adding multiple variables in an object - need further assistance

First thing first - upon suggestions, please do your best to be understandable for newbies as if it is too complex, it may not be much useful as I need to furtherly continue after the current ask. Thank you in advance for that :)
I'm trying to define an object with multiple variables that I may use.
So far I was able to create the basic class for myself (with just ID of the object), but I am now struggling to add the rest of the variables needed for the object.
The data that I have to store with the multiple instance of the object is as follows:
id of the user - this is the value thru which I need to be searching thru the objects as I will have multiple entries of the below example data for different time intervals that I need to count. It does not need to be changed within the objects variables.
Name - The name of the person for whom I will be counting the hours spent. It is static (does not need to be changed within the objects variables).
Started timestamp and Ended timestamp - The time within which the person has executed things. As I will have multiple instances of data coming towards the object, I need to check for overlapping of shifts and if so, such hours to be avoided, but if extra hours beside the overlapped - to be added. E.g. if overlapping is not a perfect match, then the additional time spent to be added to the "total spent hours". The data received for both timestamps are in format that I convert into datatime with "datetime.strptime(start, '%Y-%m-%dT%H:%M:%S+02:00')
Schedule ID - it is the ID of the entry for the started and ended timestamps. It may be saved as an array as it will not be used except for reporting purposes - e.g. the person has processed things during it's first shift (start_timestamp thru end_timestamp).
Array of contacts that I need to separate to two different values - one for e-mail, other for phone number (including country code). The array returns as [email, country_code, phone_number]
Quote of example data that I have:
PersonID: ID1234
Name: Anton Todorov
Started at: 2022-12-26T00:00:00+02:00
Ended at: 2022-12-26T02:00:00+02:00
Schedule ID: SCHEDID1
Contacts: ['a.todorov#e-mail.email', 359, '000000000']
---===Separator===---
PersonID: ID5678
Name: Morgan Freeman
Started at: 2022-12-26T02:00:00+02:00
Ended at: 2022-12-26T14:00:00+02:00
Schedule ID: SCHEDID2
Contacts: ['slogan#draftkings.com', 1, '0000000000']
---===Separator===---
PersonID: ID1234
Name: Anton Todorov
Started at: 2022-12-26T14:00:00+02:00
Ended at: 2022-12-27T02:00:00+02:00
Schedule ID: SCHEDID3
Contacts: ['a.todorov#e-mail.email', 359, '000000000']
So with that on, I have to calculate the total hours that each person has spend from within these sections of data that I have.
The object that I have so far is as follows:
class DataItem(object):
def __init__(self, person_id):
self._person_id = person_id
self._updatable_id = ""
#property
def person_id(self):
return self._person_id
#property
def updatable_id(self):
return self._updatable_id
#updatable_id.setter
def updatable_id(self, value):
self._updatable_id = value
#updatable_id.deleter
def updatable_id(self):
del self._updatable_id
class Persons(object):
def __init__(self):
self._ids = []
def find_person_by_id(self, person_id):
# search by id
existing = [i for i in self._ids if i.person_id == person_id]
if not existing:
# create and append
existing_person = DataItem(id)
self._ids.append(existing_person)
else:
# assign to existing
existing_person = existing[0]
# return the object to be acted upon
return existing_person
So.. Would someone be able to assist me with furtherly developing the object so that I may be storing the data properly inside of each of its instances, please?
I would gladly appreciate all detailed suggestions (especially as soon as I am also able to understand them).
Thank you all in advance!
I finally developed what I was looking for.
A bit messy, but that does exactly what I need.
Thanks to #Robert Lee for the attempt, despite it was not what I chose to continue with.
class PersonData(object):
def __init__(self, email, country_code, phone_number, user_id, names):
self._email = email
self._country_code = country_code
self._phone_number = phone_number
self._user_id = user_id
self._names = names
self._started = []
self._ended = []
self._schedule_id = []
#property
def email(self):
return self._email
#property
def country_code(self):
return self._country_code
#property
def phone_number(self):
return self._phone_number
#property
def user_id(self):
return self._user_id
#property
def names(self):
return self._names
#property
def started(self):
return self._started
#started.setter
def started(self, started):
self._started.append(started)
#started.deleter
def started(self):
del self._started
#property
def ended(self):
return self._ended
#ended.setter
def ended(self, ended):
self._ended.append(ended)
#ended.deleter
def ended(self):
del self._ended
#property
def schedule_id(self):
return self._schedule_id
#schedule_id.setter
def schedule_id(self, schedule_id):
self._schedule_id.append(schedule_id)
#schedule_id.deleter
def schedule_id(self):
del self._schedule_id
class PeopleBuffer(object):
def __init__(self):
self._people = []
def find_by_id(self, email, country_code, phone_number, user_id, names):
# search by id
existing = [i for i in self._people if i.user_id == user_id]
if not existing:
# create and append if not found
existing_person = PersonData(email, country_code, phone_number, user_id, names)
self._people.append(existing_person)
else:
# assign to existing
existing_person = existing[0]
# return an object to be acted upon
return existing_person
def return_all(self):
for each_person in self._people:
print("each_person: ")
print("Email: %s" % each_person.email)
print("Country Code: %s" % each_person.country_code)
print("Phone Number: %s" % each_person.phone_number)
print("User Id: %s" % each_person.user_id)
print("Names: %s" % each_person.names)
print("Started: %s" % each_person.started)
print("Ended: %s" % each_person.ended)
print("ScheduleId: %s" % each_person.schedule_id)
class MainApplication(object):
def __init__(self):
self._buffer = PeopleBuffer()
def _populate_person(self, email, country_code, phone_number, user_id, names, started, ended, schedule_id):
person = self._buffer.find_by_id(email, country_code, phone_number, user_id, names)
person.started.append(started)
person.ended.append(ended)
person.schedule_id.append(schedule_id)
def _print_people(self):
self._buffer.return_all()
def main(self):
while input("Continue? ") != "No":
user_id = input("Enter UserId: ")
names = input("Enter Name: ")
started = input("Enter Started: ")
ended = input("Enter Ended: ")
schedule_id = input("Enter ScheduleId: ")
email = input("Enter Email: ")
country_code = input("Enter CountryCode: ")
phone_number = input("Enter PhoneNumber: ")
self._populate_person(email, country_code, phone_number, user_id, names, started, ended, schedule_id)
self._print_people()
def main():
app = MainApplication()
app.main()
if __name__ == '__main__':
main()
Based on what you are doing I would consider doing json data format and do something like this. Please excuse my quick and dirty code but I think fundamentally you are looking for a way to create a data format that might work for your scenario.
Looking over it one more time, I feel like this might be the format you are looking for
[
{
"person_id": "ID1234",
"name": "Anton Todorov",
"schedule": [
{
"schedule_id": "SCHEDID1",
"started_at": "2022-12-26T00:00:00+02:00",
"ended_at": "2022-12-26T02:00:00+02:00"
},
{
"schedule_id": "SCHEDID3",
"started_at": "2022-12-26T14:00:00+02:00",
"ended_at": "2022-12-27T02:00:00+02:00"
}
],
"contact_info": {
"email": "a.todorov#e-mail.email",
"country_code": 359,
"phone_number": "000000000"
}
},
{
"person_id": "ID5678",
"name": "Morgan Freeman",
"schedule": [
{
"schedule_id": "SCHEDID2",
"started_at": "2022-12-26T02:00:00+02:00",
"ended_at": "2022-12-26T14:00:00+02:00"
}
],
"contact_info": {
"email": "slogan#draftkings.com",
"country_code": 1,
"phone_number": "000000000"
}
}
]
Code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import json
def main():
people = list()
person = dict()
person['person_id'] = 'ID1234'
person['name'] = 'Anton Todorov'
person['schedule'] = list()
schedule = dict()
schedule['schedule_id'] = 'SCHEDID1'
schedule['started_at'] = '2022-12-26T00:00:00+02:00'
schedule['ended_at'] = '2022-12-26T02:00:00+02:00'
person['schedule'].append(schedule)
schedule = dict()
schedule['schedule_id'] = 'SCHEDID3'
schedule['started_at'] = '2022-12-26T14:00:00+02:00'
schedule['ended_at'] = '2022-12-27T02:00:00+02:00'
person['schedule'].append(schedule)
contact_info = dict()
contact_info['email'] = 'a.todorov#e-mail.email'
contact_info['country_code'] = 359
contact_info['phone_number'] = '000000000'
person['contact_info'] = contact_info
people.append(person)
person = dict()
person['person_id'] = 'ID5678'
person['name'] = 'Morgan Freeman'
person['schedule'] = list()
schedule = dict()
schedule['schedule_id'] = 'SCHEDID2'
schedule['started_at'] = '2022-12-26T02:00:00+02:00'
schedule['ended_at'] = '2022-12-26T14:00:00+02:00'
person['schedule'].append(schedule)
contact_info = dict()
contact_info['email'] = 'slogan#draftkings.com'
contact_info['country_code'] = 1
contact_info['phone_number'] = '000000000'
person['contact_info'] = contact_info
people.append(person)
print(json.dumps(people, indent=4))
if __name__ == '__main__':
main()
First things first; Your example feels more like a Java example written in Python. Instance variables are in practice properties, so all of your #property methods is redundant code.
dataclasses module is implemented for cases like yours. For example,
Schedule data class:
# schedule.py
from dataclasses import dataclass, field
from datetime import datetime
from typing import Union
#dataclass(unsafe_hash=True)
class Schedule:
schedule_id: str
start: Union[str, datetime]
end: Union[str, datetime]
def __post_init__(self):
self.start = self._datetime_converter(self.start)
self.end = self._datetime_converter(self.end)
def _datetime_converter(self, dt: str):
return datetime.strptime(dt, '%Y-%m-%dT%H:%M:%S+02:00')
def overlaps_with(self, other: Schedule):
return not ((self.start >= other.end) ^ (other.start >= self.end))
#property
def deltatime(self):
return self.end - self.start
Worker data class:
# worker.py
from functools import reduce
from dataclasses import dataclass, field
from .schedule import Schedule
#dataclass
class Worker:
person_id: str
name: str
contacts: str = field(repr=False)
schedules: set[Schedule] = field(init=False, default_factory=set)
email: str = field(init=False, default='')
country: int = field(init=False, default=-1)
phone: str = field(init=False, default='')
def __post_init__(self):
self.email, self.country, self.phone = self.contacts
#classmethod
def add_from_json(cls, worker_id: str, file: str):
NotImplemented
def overlapping_schedules(self, schedule: Schedule):
return filter(lambda s: s.overlaps_with(schedule), self.schedules)
def schedule_overlaps(self, schedule: Schedule):
return any(self.overlapping_schedules(schedule))
def add_schedule(self, schedule: Schedule):
if not self.schedule_overlaps(schedule):
self.schedules.update({schedule})
else:
NotImplemented
def add_schedules_from_json(self, file: str):
NotImplemented
def remove_schedule(self, schedule: Schedule):
self.schedules -= {schedule}
#property
def total_time_scheduled(self):
deltas = map(lambda s: s.deltatime, self.schedules)
return reduce(lambda x, y: x + y, deltas)
def __hash__(self):
return hash(id(self.person_id))
def __eq__(self, other):
return self.person_id == other.person_id
In theory you don't really need to create a Workers class. The methods __hash__ and __eq__ take care of that and you just need to create a set[Worker]. However, if you still want to create a Workers class, just loop over the existing methods.

How do you get value from another class?

I am starting to learn about object-oriented programming and I'm having a rough time with it.
I have some questions about it:
class Record:
"""Represent a record."""
def __init__(self, category, name, value):
self._category = category
self._name = name
self._value = value
# 1. Define the formal parameters so that a Record can be instantiated
# by calling Record('meal', 'breakfast', -50).
# 2. Initialize the attributes from the parameters. The attribute
# names should start with an underscore (e.g. self._amount)
#property
def amount (self):
return self._value
# Define getter methods for each attribute with #property decorator.
# Example usage:
# >>> record = Record('meal', 'breakfast', -50)
# >>> record.amount
# -50
class Records:
"""Maintain a list of all the 'Record's and the initial amount of money."""
def __init__(self):
# 1. Read from 'records.txt' or prompt for initial amount of money.
# 2. Initialize the attributes (self._records and self._initial_money)
# from the file or user input.
self._records =[]
self._money = 0
fh = open(file='records.txt', mode='r')
if ',' not in i:
self._money = int(i.strip())
else:
raw_line = i.strip()
category, activity, value = raw_line.split(',')
x = Record(category, activity, value)
self._records.append(x)
print("Welcome back!")
fh.close()
#property
def view(self):
for i in self._records:
print(Record._category)
#1. Print all the records and report the balance.
x = Records()
Records.view()
I wanted to print out value from my newly acquired list but I don't know how to pass my data from class Records to class Record for processing. How should I approach this?

Dictionary to "inherit" from class of objects that I use it to store; e.g. my_dict['item1'].method1() or mydict['item1'].property1 = 'new'

I've searched for couple days now but couldn't find anything so maybe it's not possible.
As in topic, is there a way for a dict to "inherit" from a class so all the methods and properties are visible via "intelisens" ?
Example
class Word(object):
def __init__(self, word):
self.word = word
self.base_word = ''
self.derived_words = set()
self.sub_words = set()
self.frequency = 0
def method_1(self):
do something here
def method_2(self):
do something else here
myDict['computer'] = Word('computer')
myDict['notebook'] = Word('notebook')
and then i can obviously do this and it will work
mydict['computer'].method_2()
mydict['notebook'].frequency = 12
but i'd like to know if there is a way to make myDict object know that this methods and properties of the object are available and they would show up in "intelisense".
Picture Example
I'm using PyCharm.
Best regards
Bartek
PyCharm does support type hinting. I'm assuming you are using Python 2.7 since you are inheriting object. I have 3 ways you could do this in Python 2.7.
1
myDict['computer'] = Word('computer')
computer1 = myDict['computer'] # type: Word
2
computer2 = myDict['computer']
""":type : Word """
Now when you use computer1 or computer2 you should get the drop down that you like.
3
If your dictionary always returns a Word object you could create a dict object which inherits from dict, and make a function which returns Word type using rtype in the method docstring.
class Word(object):
def __init__(self, word):
self.word = word
def foo(self):
return self.word
class FooDict(dict):
def __init__(self, *arg, **kw):
super(FooDict, self).__init__(*arg, **kw)
def get_item(self, item):
"""
My docstring.
:return: a class of the type I want.
:rtype: Word
"""
return self[item]
a = Word("foo")
d = FooDict()
d['computer'] = a
computer3 = d.get_item('computer')
Now computer3 should behave how you like.
Reposting user2235698 answer from comment to my first post, for future googlers
Just add type hint to d definition.
It will be like d = {} # type: typing.Dict[str, Word] or d: typing.Dict[str, Word] = {} (since Python 3.6) – user2235698 21 hours ago
so now code looks like this:
import typing
class Word():
def __init__(self, word):
self.word = word
self.base_word = ''
self.derived_words = set()
self.sub_words = set()
self.meaning = ''
self.reading = ''
self.frequency = ''
self.sub_chars = set()
def analyze_sub_words(self):
print('working')
myComputer = Word('computer')
d: typing.Dict[str, Word] = {}
d['computer'] = myComputer
# now hints are being displayed
d['computer'].base_word

comparing a number with a prefix in a class in python

I am working on a small module.
I have a class called pricelist. It contains the attributes prefix and price.
class pricelist:
def setPrefix(self,prefix):
self.prefix = prefix
def setPrice(self,price):
self.price = price
def getPrefix(self):
return self.prefix
def getPrice(self):
return self.price
def getPrefixLength(self):
return len(self.prefix)
I have created a list of pricelist objects.
now i want to perform the operation, where when i give a phone number as input, i want to find prefix of the number and the corresponding price related to the prefix.
Example:
prefix price
46 2.0
44 3.0
.
.
.
.
when i give the input say "46 7223232323", it should return the price corresponding to it.
I am new to python and python classess, so can you please help me out with the logic of this
Keep the prefixes and prices in a dict:
self.data = {"46": 2.0,"44":3.0}
inp = "46 7223232323"
get_pre = inp.split()[0]
print (self.data.get(get_pre))
2.0
class Pricelist:
def __init__(self):
self.data = {"46": 2.0,"44":3.0}
p = Price_list()
inp = "46 7223232323"
get_pre = inp.split()[0]
print (p.data.get(get_pre,"Sorry that is an invalid prefix")) # if the prefix does not exist, it will output "Sorry that is an invalid prefix"
You can access attributes directly without getters and setters:
class Price_list:
def __init__(self,prefix,price):
self.prefix = prefix
self.price = price
self.pre_len = len(self.prefix)
p = Price_list("46", "4.99")
print (p.prefix, p.price, p.pre_len)
You can use data as a class attribute and add all instance attributes to it:
class Price_list:
data = {}
def __init__(self, prefix, price):
self.prefix = prefix
self.price = price
self.pre_len = len(self.prefix)
p = Price_list("46", "4.99")
p.data[p.prefix] = [p.price]
p1 = Price_list("44", "3.99")
p1.data[p1.prefix] = [p1.price]

Categories