Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a custom JSON decoder for a complex object?

Tags:

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.

like image 862
lmotl3 Avatar asked Feb 26 '18 15:02

lmotl3


People also ask

How do you decode a class object in Python?

For encoding, we use json. dumps() and for decoding, we'll use json. loads() . So it is obvious that the dumps method will convert a python object to a serialized JSON string and the loads method will parse the Python object from a serialized JSON string.

How do I decode a JSON file?

You just have to use json_decode() function to convert JSON objects to the appropriate PHP data type. Example: By default the json_decode() function returns an object. You can optionally specify a second parameter that accepts a boolean value. When it is set as “true”, JSON objects are decoded into associative arrays.

What is JSON Jsonencoder?

An object that encodes instances of a data type as JSON objects.


1 Answers

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
like image 73
VirtualScooter Avatar answered Oct 22 '22 08:10

VirtualScooter