3

Pythonのjsonpickle0.4.0を、カスタムオブジェクトを含むカスタムオブジェクトに「再帰」させるのに問題があります。これが私の問題を示すサンプルコードです。

import jsonpickle
import jsonpickle.handlers

class Ball(object):
    def __init__(self, color):
        self.color = color

class Box(object):
    def __init__(self, *args):
        self.contents = args

class BallHandler(jsonpickle.handlers.BaseHandler):    
    def flatten(self, obj, data):
        data['color'] = obj.color
        return data

class BoxHandler(jsonpickle.handlers.BaseHandler):    
    def flatten(self, obj, data):
        data['contents'] = obj.contents
        return data

jsonpickle.handlers.registry.register(Ball, BallHandler)
jsonpickle.handlers.registry.register(Box, BoxHandler)

# works OK -- correctly prints: {"color": "white"}
white_ball = Ball('white')
print jsonpickle.encode(white_ball, unpicklable=False)

# works OK -- correctly prints: [{"color": "white"}, {"color": "green"}]
green_ball = Ball('green')
balls = [white_ball, green_ball]
print jsonpickle.encode(balls, unpicklable=False)

# works OK -- correctly prints: {"contents": [1, 2, 3, 4]}
box_1 = Box(1, 2, 3, 4)
print jsonpickle.encode(box_1, unpicklable=False)

# dies with "Ball object is not JSON serializable"
box_2 = Box(white_ball, green_ball)
print jsonpickle.encode(box_2, unpicklable=False)

ボールには「色」があり、ボックスには「内容」があります。ボールの[ネイティブ]配列がある場合は、jsonpickle機能します。[ネイティブ]intのボックスがある場合は、jsonpickle機能します。

しかし、私がボールの箱を持っている場合、jsonpickle爆弾は"Ball object is not JSON serializable"

スタックトレースから、エンコーダーがjsonpickle他のJSONライブラリーに移動しているという予感があります...それは明らかに私がBallHandlerを登録したことを知りません。

どうすればこれを修正できますか?

ちなみに、私のサンプルはDjangoのどの部分も明示的に使用していませんが、Djangoアプリで動作させるにはこれが必要になります。

どんな入力でもよろしくお願いします!

4

3 に答える 3

4

まず、そもそもなぜカスタムハンドラーを作成するのですか?デフォルトのハンドラーがすでに実行しているのとまったく同じことを実行しようとしています。これらの2行を削除し、これらすべてのオブジェクトを使用して、または使用せずにregister呼び出すと、同じ結果が得られます。ただし、ボールでいっぱいのボックスで失敗するのではなく、希望どおりに機能する点が異なります。encodeunpicklable=False

チュートリアル、API、テストケース、およびサンプルを確認すると、このようなコレクションをシミュレートするカスタムハンドラーが作成されることはありません。(たとえば、テストスイート(および)の//クラスを見Nodeてください。)したがって、あなたは、予期されていなかったことを実行しようとしていて、実行可能であることを意図していないと思います。DocumentSectionsamples.pydocument_test.py

しかし、あなたの実際の質問を見てみましょう:なぜそれが機能しないのですか?

まあ、それは簡単です。あなたはそれを間違っています。のドキュメントによると、次のことを行うBaseHandler.flatten必要があります。

objをjsonに適した形式にフラット化します。

だから、これを考えると:

class BoxHandler(jsonpickle.handlers.BaseHandler):    
    def flatten(self, obj, data):
        data['contents'] = obj.contents
        return data

obj.contentsJSONに適した形式であることを効果的に約束しています。しかし、そうではありません。それlistBallオブジェクトのです。

それで、正しい答えは何ですか?ええと、あなたはあなた平らにされているのと同じ方法で内容の各要素を平らにすることができます。それを行う簡単な方法があるはずだと思うかもしれませんが、正直なところ、API、ドキュメント、サンプル、または単体テストには何も表示されないので、表示されないので、実行する必要があります。手動で。おそらくこのようなもの(テストされていない):

class BoxHandler(jsonpickle.handlers.BaseHandler):    
    def flatten(self, obj, data):
        p = jsonpickle.Pickler()
        data['contents'] = [p.flatten(elem) for elem in obj.contents]
        return data

しかし…あなたはあなたPicklerを漬けるために使用されているものと同じものを手に入れていないので-そして私はあなたができる方法を見ていません-これはおそらくmaxdepthとのunpicklableパラメータに違反するでしょうencode

したがって、これを行う正しい方法はないかもしれません。

于 2013-01-15T01:08:14.263 に答える
4

ピクルスのコンテキストにコールバックして、ピクルスを続行できると思います。

class BoxHandler(jsonpickle.handlers.BaseHandler):
def flatten(self, obj, data):
    return [self.context.flatten(x,reset=False) for x in obj.contents]

これは、組み込みの_list_recurse()関数がpickler.py:44でこのケースを処理する方法と似ているようです。これは、flatten()がself._flattenを呼び出すだけだからです(オプションで状態変数をリセットした後)。

def _list_recurse(self, obj): return [self._flatten(v) for v in obj]

私は今これをテストしているところですが、_depthは期待どおりに維持されているようです。

于 2017-06-21T02:39:09.563 に答える
2

私にはバグのように見えますが、それは原則的なものです。jsonpickleカスタムオブジェクト処理をに追加する場合はjson、コンテンツを「前処理」するのではなく、後者に統合する必要があります。abarnertが言ったように、これを自分で処理するようにユーザーに要求する現在の状態は、他のドアのIMOに責任を負わせています。

私があなたなら、これを自分で修正するか、オブジェクトをそのままJSONに適したものにします。たとえば、ネイティブのPythonデータ構造(JSONはの代替表現)のように見せます。もちろん、より簡単な方法は、そのような構成を回避することです。

于 2013-01-15T01:21:24.890 に答える