Like the title says, I'm trying to write a custom decoder for an object whose class I've defined which contains other objects who class I've defined. The "outer" class is an Edge, defined like so:
class Edge:
def __init__(self, actor, movie):
self.actor = actor
self.movie = movie
def __eq__(self, other):
if (self.movie == other.movie) & (self.actor == other.actor):
return True
else:
return False
def __str__(self):
print("Actor: ", self.actor, " Movie: ", self.movie)
def get_actor(self):
return self.actor
def get_movie(self):
return self.movie
with the "inner" classes actor and movie defined like so:
class Movie:
def __init__(self, title, gross, soup, year):
self.title = title
self.gross = gross
self.soup = soup
self.year = year
def __eq__(self, other):
if self.title == other.title:
return True
else:
return False
def __repr__(self):
return self.title
def __str__(self):
return self.title
def get_gross(self):
return self.gross
def get_soup(self):
return self.soup
def get_title(self):
return self.title
def get_year(self):
return self.year
class Actor:
def __init__(self, name, age, soup):
self.name = name
self.age = age
self.soup = soup
def __eq__(self, other):
if self.name == other.name:
return True
else:
return False
def __repr__(self):
return self.name
def __str__(self):
return self.name
def get_age(self):
return self.age
def get_name(self):
return self.name
def get_soup(self):
return self.soup
(soup is just a beautifulsoup object for that movie/actor's Wikipedia page, it can be ignored). I've written a customer encoder for the edge class as well:
class EdgeEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, Edge):
return {
"Actor": {
"Name": o.get_actor().get_name(),
"Age": o.get_actor().get_age()
},
"Movie": {
"Title": o.get_movie().get_title(),
"Gross": o.get_movie().get_gross(),
"Year": o.get_movie().get_year()
}
}
return json.JSONEncoder.default(self, o)
which I've tested, and it properly serializes a list of edges into a JSON file. Now my problem comes when trying to write an edge decoder. I've used the github page here as a reference, but my encoder deviates from his and I'm wondering if it's necessary to change it. Do I need to explicitly encode an object's type as its own key-value pair within its JSON serialization the way he does, or is there some way to grab the "Actor" and "Movie" keys with the serialization of the edge? Similarly, is there a way to grab "Name". "Age", etc, so that I can reconstruct the Actor/Movie object, and then use those to reconstruct the edge? Is there a better way to go about encoding my objects instead? I've also tried following this tutorial, but I found the use of object dicts confusing for their encoder, and I wasn't sure how to extend that method to a custom object which contains custom objects.
The encoder/decoder example you reference (here) could be easily extended to allow different types of objects in the JSON input/output.
However, if you just want a simple decoder to match your encoder (only having Edge objects encoded in your JSON file), use this decoder:
class EdgeDecoder(json.JSONDecoder):
def __init__(self, *args, **kwargs):
json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, dct):
if 'Actor' in dct:
actor = Actor(dct['Actor']['Name'], dct['Actor']['Age'], '')
movie = Movie(dct['Movie']['Title'], dct['Movie']['Gross'], '', dct['Movie']['Year'])
return Edge(actor, movie)
return dct
Using the code from the question to define classes Movie
, Actor
, Edge
, and EdgeEncoder
, the following code will output a test file, then read it back in:
filename='test.json'
movie = Movie('Python', 'many dollars', '', '2000')
actor = Actor('Casper Van Dien', 49, '')
edge = Edge(actor, movie)
with open(filename, 'w') as jsonfile:
json.dump(edge, jsonfile, cls=EdgeEncoder)
with open(filename, 'r') as jsonfile:
edge1 = json.load(jsonfile, cls=EdgeDecoder)
assert edge1 == edge