web-dev-qa-db-fra.com

Comment écrire un décodeur JSON personnalisé pour un objet complexe?

Comme le titre l'indique, j'essaie d'écrire un décodeur personnalisé pour un objet dont j'ai défini la classe et qui contient d'autres objets que j'ai définis. La classe "externe" est un Edge, défini comme suit:

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

avec l'acteur et le film des classes "internes" définis comme suit:

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

(la soupe n'est qu'un bel objet pour la page Wikipedia de ce film/acteur, elle peut être ignorée). J'ai également écrit un encodeur client pour la classe Edge:

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)

que j'ai testé, et il sérialise correctement une liste d'arêtes dans un fichier JSON. Maintenant, mon problème survient lorsque j'essaie d'écrire un décodeur Edge. J'ai utilisé la page github ici comme référence, mais mon encodeur s'écarte du sien et je me demande s'il est nécessaire de le changer. Dois-je coder explicitement le type d'un objet comme sa propre paire clé-valeur dans sa sérialisation JSON comme il le fait, ou existe-t-il un moyen de récupérer les clés "Acteur" et "Film" avec la sérialisation du bord? De même, existe-t-il un moyen de saisir "Nom". "Age", etc., pour que je puisse reconstruire l'objet Acteur/Film, puis les utiliser pour reconstruire le bord? Y a-t-il une meilleure façon de coder mes objets à la place? J'ai également essayé de suivre le tutoriel this , mais j'ai trouvé l'utilisation de dict d'objets déroutant pour leur encodeur, et je ne savais pas comment étendre cette méthode à un objet personnalisé qui contient des objets personnalisés.

17
lmotl3

L'exemple d'encodeur/décodeur auquel vous faites référence ( ici ) pourrait être facilement étendu pour autoriser différents types d'objets dans l'entrée/sortie JSON.

Cependant, si vous souhaitez simplement qu'un décodeur simple corresponde à votre encodeur (ayant uniquement des objets Edge encodés dans votre fichier JSON), utilisez ce décodeur:

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

En utilisant le code de la question pour définir les classes Movie, Actor, Edge et EdgeEncoder, le code suivant affichera un fichier de test, puis le lira de retour dans:

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
1
VirtualScooter