Serialize a list of custom objects - python

I am creating an list of custom objects from a database. The custom object class and the list creation is shown below. How can I serialize the list holding this custom data?
class MyCustomObject():
"""For displaying the records"""
def __init__(self):
self.rec_id = ""
self.place = ""
rec_list = [] #The List
# Creating a list of MyCustomObject's from rows
for col in rows:
rec = MyCustomObject()
rec.rec_id = col[0]
rec.place = col[1]
rec_list.append(recently_viewed)
How can I serialize this list of MyCustomObjects to JSON.

you could try writing your own serializer as below:
import json
class MyCustomObject():
"""For displaying the records"""
def __init__(self):
self.rec_id = ""
self.place = ""
class mySerializer(json.JSONEncoder):
def default(self, obj):
return obj.__dict__
rec_list = [] #The List
# Creating a list of MyCustomObject's from rows
rows = [[1,2],[3,4]]
for col in rows:
rec = MyCustomObject()
rec.rec_id = col[0]
rec.place = col[1]
rec_list.append(rec)
print [json.dumps(r, cls=mySerializer) for r in rec_list]
output:
['{"place": 2, "rec_id": 1}', '{"place": 4, "rec_id": 3}']

By extending JsonEncoder (https://docs.python.org/2/library/json.html#json.JSONEncoder)
So you will get something like;
import json
class MyJsonEncoder
def default(self, obj):
if isinstance(obj, MyCustomObject):
return {} # dict representation of your object
return super(MyJsonEncoder, self).dumps(obj)
json.dumps(rec_list, encoder=MyJsonEncoder)

Related

Add objects from a class to class list and extract them

I have a class:
class AlchemicalElement:
def __init__(self, name: str):
self.name = name
I then create a class that will be used to store the AlchemicalElement objects:
class AlchemicalStorage:
def __init__(self):
self.storage_list = []
I don't understand how to write this function:
def extract(self) -> list[AlchemicalElement]:
"""Return a list of all the elements from storage and empty the storage itself."""
return []
So far I've only managed this:
def add(self, element: AlchemicalElement):
if isinstance(element, AlchemicalElement):
self.storage_list.append(element)
else:
raise TypeError()
This is what I try to achieve:
storage = AlchemicalStorage()
storage.add(AlchemicalElement('Water'))
storage.add(AlchemicalElement('Fire'))
storage.extract() # -> [<AE: Water>, <AE: Fire>]
storage.extract() # -> []
Question
How do I write the .extract() method so that when first called it returns a list of elements in the storage_list, but an empty list on any call after?
Assuming this is what you're starting with
class AlchemicalElement:
def __init__(self, name: str):
self.name = name
class AlchemicalStorage:
def __init__(self):
self.storage_list = []
def add(self, element: AlchemicalElement):
if isinstance(element, AlchemicalElement):
self.storage_list.append(element)
else:
raise TypeError()
def extract(self) -> list[AlchemicalElement]:
"""Return a list of all the elements from storage and empty the
storage itself."""
return []
if __name__ == "__main__":
storage = AlchemicalStorage()
storage.add(AlchemicalElement('Water'))
storage.add(AlchemicalElement('Fire'))
print(storage.extract()) # -> [<AE: Water>, <AE: Fire>]
print(storage.extract()) # -> []
You'll currently get output that looks like this
[]
[]
Your first question is how do I make the .extract() method exhaust the .storage_list?
We could do something like this:
def extract(self) -> list[AlchemicalElement]:
"""Return a list of all the elements from storage and empty the
storage itself."""
# copy the storage_list into out_list and then clear it
# only works once!
out_list = self.storage_list.copy()
self.storage_list.clear()
return out_list
Now if we run the script we should get the following:
[<__main__.AlchemicalElement object at 0x000001CB12CBBD00>, <__main__.AlchemicalElement object at 0x000001CB12CBBCA0>]
[]
Now maybe to answer another question, how to do I get it to return <AE: Water> instead of <__main__.AlchemicalElement object at ...>?
We can do that by adding a __repr__ method to the AlchemicalElement class
class AlchemicalElement:
def __init__(self, name: str):
self.name = name
def __repr__(self):
return f"<AE: {self.name}>"
All code together now looks like this
class AlchemicalElement:
def __init__(self, name: str):
self.name = name
def __repr__(self):
return f"<AE: {self.name}>"
class AlchemicalStorage:
def __init__(self):
self.storage_list = []
def add(self, element: AlchemicalElement):
if isinstance(element, AlchemicalElement):
self.storage_list.append(element)
else:
raise TypeError()
def extract(self) -> list[AlchemicalElement]:
"""Return a list of all the elements from storage and empty the
storage itself."""
# copy the storage_list into out_list and then clear it
# only works once!
out_list = self.storage_list.copy()
self.storage_list.clear()
return out_list
if __name__ == "__main__":
storage = AlchemicalStorage()
storage.add(AlchemicalElement('Water'))
storage.add(AlchemicalElement('Fire'))
print(storage.extract()) # -> [<AE: Water>, <AE: Fire>]
print(storage.extract()) # -> []
And output looks like how you want
[<AE: Water>, <AE: Fire>]
[]
Note:
The __repr__ method should return a printable representation of the object, most likely one of the ways possible to create this object. source

How to refresh an instance variable list in a method within the same class in Python?

I have a class that has a list inside the __init__ method that grabs a list of items from an API and stores it to the instance variable. Below I have named this list self.mylist
class Test:
def __init__(self):
self.mylist = []
getlist = api.getValue('system/list')
if isinstance(getlist, Error.restError):
logger.error("ERROR: %s", _ret)
self.mylist = []
else:
self.mylist = getlist[0]
def getListObj(self, cname):
for obj in self.mylist:
if obj.get('CNAME', '') == cname:
return obj
The issue I have now is that the list remains the same even if any of the parameters change in one of the object in that list. I want to refresh the list every time the getListObj method is called. I tried this below but it did not work. Basically trying to reset self.mylist by calling the API again and making sure I have the latest values in the list.
class Test:
def __init__(self):
self.mylist = []
getlist = api.getValue('system/list')
if isinstance(getlist, Error.restError):
logger.error("ERROR: %s", _ret)
self.mylist = []
else:
self.mylist = getlist[0]
def getListObj(self, cname):
getlist = api.getValue('system/list')
if isinstance(getlist, Error.restError):
logger.error("ERROR: %s", _ret)
self.mylist = []
else:
self.mylist = getlist[0]
for obj in self.mylist:
if obj.get('CNAME', '') == cname:
return obj

How to write Python objects to a JSON file?

I have three classes in my program,
Processes contain Lanes.
Lanes contain Tasks.
The objects are stored in each other as arrays with some other string information about the class
I'm trying to output the Process objects into JSON files but I keep getting the following error:
Object of type 'Process' is not JSON serializable
I'm fairly new to JSON processing so I don't understand why the objects are outputting this error.
I'm using the following code to print the Process objects as JSON items:
def outputJSON(self):
for n in self.processes:
print(json.dumps(n, sort_keys=True, indent=4))
And these are the class objects:
class Task(object):
def __init__(self, name, UiD):
self.name = name
self.UiD = UiD
self.incoming = 'None'
self.outgoing = []
self.messageFlowIn = 'None'
self.messageFlowOut = 'None'
def __str__(self):
print(self.name +"\n")
print("Incoming Connection : \n" + self.incoming + "\n")
print("Outgoing Connections : ")
if len(self.outgoing) >= 1:
for n in self.outgoing:
print(n+"\n")
print("MessageFlowIn : " + self.messageFlowIn)
print("MessageFlowOut : " + self.messageFlowOut)
class Lane(object):
def __init__(self, name, associatedTasks):
self.name = name
self.associatedTasks = associatedTasks
class Process(object):
def __init__(self, name, associatedLaneNames):
self.name = name
self.associatedLaneNames = associatedLaneNames
self.laneObjects = []
How can I correctly output this data to a JSON file?
Assuming that your attributes are simple values or list, you can use the class .__dict__ attribute to convert the class to a dict and then serialize it to a json, as a example:
p = Process('name', 'lanename')
p.laneObjects.extend([Lane('name', [Task('name', 'uid')])])
def to_dict(obj):
output ={}
for key, item in obj.__dict__.items():
if isinstance(item, list):
l = []
for item in item:
d = to_dict(item)
l.append(d)
output[key] = l
else:
output[key] = item
return output
to_dict(p)
This outputs:
{'associatedLaneNames': 'lanename',
'laneObjects': [{'associatedTasks': [{'UiD': 'uid',
'incoming': 'None',
'messageFlowIn': 'None',
'messageFlowOut': 'None',
'name': 'name',
'outgoing': []}],
'name': 'name'}],
'name': 'name'}
as a dict.
Following #czr 's example; the solution was to go through and convert the object mappings into explicit dictionaries and then output an array of those nested dictionaries as follows:
def serialize(self):
processArray =[]
decisionItems = []
for process in self.processes:
processObj = {}
laneArray = []
for lane in process.laneObjects:
laneObj = {}
taskArray = []
for task in lane.associatedTasks:
taskObj = {}
for variableName, value in task.__dict__.items():
temp = value
placeHolderArray =[]
if isinstance(value, Task) or isinstance(value, StartEvent) or isinstance(value, EndEvent):
temp = value.name
taskObj[variableName] = temp
elif isinstance(value,list): #fix these lines
for n in value:
if isinstance(n, Task) or isinstance(n, StartEvent) or isinstance(n, EndEvent) or isinstance(n, DecisionBranch):
placeHolderArray.append(n.name)
taskObj[variableName] = placeHolderArray
elif isinstance(value, DecisionBranch):
taskObj['junctionPath'] = task.junctionPath
decisionBranch = {}
decisionBranch['name'] = value.name
decisionBranch['options'] = value.decisionBranch
decisionBranch['source'] = value.source
#taskObj['DecisionTree'] = decisionBranch
taskObj['URLs'] = task.urls
taskArray.append(taskObj)
laneObj['name'] = lane.name
laneObj['associatedTasks'] = taskArray
laneArray.append(laneObj)
processObj['name'] = process.name
processObj['laneObjects'] = laneArray
processArray.append(processObj)
return processArray
def outputJsonFile(self, fileContents):
tempString = self.fileName.split('.')
outputName = tempString[0]+'.json'
with open(outputName, 'w') as outfile:
outfile.write(json.dumps(fileContents, sort_keys=True, indent=4))
where fileContents takes the returned processArray from serialize()
The takeaway note is that iterating through a classes variables as follows:
for variableName, value in objectName.__dict__.items():
Allows you to retrieve the class variables and their respective values as a dictionary. As you is my code above, if it so happens that your value is an object then you have to explicitly define what properties you want to retrieve from that object by using python's handy isinstance():
if isinstance(value, Task) or isinstance(value, StartEvent) or isinstance(value, EndEvent):
temp = value.name
taskObj[variableName] = temp
If it is a python classic object, such as list; make sure to iterate through it explicitly:
elif isinstance(value,list): #fix these lines
for n in value:
if isinstance(n, Task) or isinstance(n, StartEvent) or isinstance(n, EndEvent) or isinstance(n, DecisionBranch):
placeHolderArray.append(n.name)
taskObj[variableName] = placeHolderArray
The reason for my initial confusion was my prior experience with google's GSON for Java. That handy api simply takes a Java class and handily outputs a JSON formatted file for you. Unfortunately Python does not have such native libraries. The ideal solution would be to design your classes in ways such that they only contained native data types like strings,ints or lists of native datatypes. that way simply iterating over objectName.__dict__.item() will provide a neat enough solution for you.
Try using pickle to serialize and write to .JSON https://docs.python.org/3/library/pickle.html
Here's an example of how I save my data for a game I'm working on using pickle
def save():
file_player = "Save/%s/player.JSON" % functions.player.name
file_world = "Save/%s/world.JSON" % functions.player.name
file_counter = "Save/%s/counter.JSON" % functions.player.name
a = functions.player
b = world.world_map
c = functions.counter
save_player = open(file_player, 'wb')
save_world = open(file_world, 'wb')
save_counter = open(file_counter, 'wb')
pickle.dump(a, save_player, -1)
pickle.dump(b, save_world, -1)
pickle.dump(c, save_counter, -1)
save_player.close()
save_world.close()
save_counter.close()
This is saving 3 different classes from 3 different files, and dumping them into 1 save folder.

Save dictionary of class instances in a file?

Consider following class and function:
class DictEntry:
term = ""
docFreq = 0
termFreq = 0
postingList = []
def __init__(self, t, df, tf, pl):
self.term = t
self.docFreq = df
self.termFreq = tf
self.postingList = pl
def insertDict(docId, token, termFreq, dictionary):
entry = dictionary.get(token)
if entry is None:
postList = []
entry = DictEntry(token, 0, 0, postList)
dictionary[token] = entry
entry.docFreq +=1
entry.postingList.extend([docId])
entry.termFreq += termFreq
The function insertDict creates an object for a key if it's not in dictionary.
In main, I need to save dictionary with keys and class instances to a file.

printing objects in python

I'm trying to print these car_object[objectname] objects, but not sure how to do it....
I also have a Cars class. When I do print(car_object[objectname]) I get ObjectmeA160
<__main__.Cars object at 0x027FB970>. what am I doing wrong?
def __iter__(self):
car_object = {}
cursor = self._db.execute('SELECT IDENT, MAKE, MODEL, DISPLACEMENT,
POWER, LUXURY FROM CARS')
for row in cursor:
car_object = {}
objectname = 'Object'+str(row['IDENT'])
car_object[objectname] = Cars(ident = row['IDENT'], make = row['MAKE'],
model = row['MODEL'], disp = row['DISPLACEMENT'], power = row['POWER'], luxury = row['LUXURY'])
print(car_object[objectname])
yield dict(row)
class Cars:
def __init__(self, **kwargs):
self.variables = kwargs
def set_Variable(self, k, v):
self.variables[k] = v
def get_Variable(self, k):
return self.variables.get(k, None)
The <__main__.Cars object at 0x027FB970> is the standard string for custom objects that do not implement their own .__str__() hook. You can customize it by implementing that method:
class Cars:
# ....
def __str__(self):
return 'Car instance with variables: {!r}'.format(self.variables)

Categories

Resources