web-dev-qa-db-ja.com

複雑なオブジェクトのカスタムJSONデコーダーを作成する方法

タイトルが言うように、クラスを定義した他のオブジェクトを含む、クラスを定義したオブジェクトのカスタムデコーダーを記述しようとしています。 「外部」クラスはEdgeであり、次のように定義されています。

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

"inner"クラスの俳優と映画は次のように定義されています。

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

(スープは、その映画/俳優のWikipediaページの美しいスープオブジェクトです。無視してかまいません)。 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)

これをテストしましたが、エッジのリストをJSONファイルに適切にシリアル化します。ここで、Edgeデコーダーを作成しようとすると問題が発生します。私はgithubページ here を参照として使用しましたが、私のエンコーダは彼から逸脱しており、変更する必要があるかどうか疑問に思っています。オブジェクトのタイプを、JSONシリアライゼーション内の独自のキーと値のペアとして明示的にエンコードする必要がありますか?それとも、エッジのシリアライゼーションで「Actor」キーと「Movie」キーを取得する方法はありますか?同様に、「名前」を取得する方法はありますか。 「年齢」などで、Actor/Movieオブジェクトを再構築し、それらを使用してエッジを再構築できるようにしますか?代わりにオブジェクトをエンコードするより良い方法はありますか?私は this のチュートリアルも試しましたが、オブジェクトディクテーションの使用がエンコーダーを混乱させることに気づき、そのメソッドをカスタムオブジェクトを含むカスタムオブジェクトに拡張する方法がわかりませんでした。

17
lmotl3

参照するエンコーダー/デコーダーの例( ここ )は、JSON入出力でさまざまなタイプのオブジェクトを許可するように簡単に拡張できます。

ただし、単純なデコーダーをエンコーダーと一致させたいだけの場合(JSONファイルにエンコードされたEdgeオブジェクトのみを持つ)、このデコーダーを使用します。

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

質問のコードを使用してクラスMovieActorEdge、およびEdgeEncoderを定義すると、次のコードはテストファイルを出力し、それを読み取りますに戻る:

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