3

我无法让 Python 的 jsonpickle 0.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)

球有“颜色”,盒子有“内容”。如果我有一个 [native] Balls 数组,那么就jsonpickle可以了。如果我有一盒 [native] 整数,那么就jsonpickle可以了。

但如果我有一个球盒,jsonpickle"Ball object is not JSON serializable".

从堆栈跟踪中,我有预感编码器正在离开jsonpickle并转到其他一些 JSON 库......显然不知道我已经注册了 BallHandler。

我该如何解决这个问题?

顺便说一句,我的示例没有明确使用 Django 的任何部分,但我需要它才能在 Django 应用程序中工作。

提前感谢您的任何意见!

4

3 回答 3

4

首先,为什么要创建自定义处理程序?您正在尝试做与默认处理程序已经做的完全相同的事情。删除这两条register线,并用所有这些对象调用encode和不调用unpicklable=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.contents采用 JSON 友好的形式。但事实并非如此;这是一个list对象Ball

那么,正确答案是什么?好吧,您可以像扁平化一样扁平化内容中的每个元素。您认为必须有一些简单的方法可以做到这一点,但老实说,我在 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的不是用来腌制你的东西——而且我看不出有什么办法——这很可能maxdepthunpicklable违反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,它应该集成到后者中,而不是尝试为它“预处理”内容。正如阿巴内特所说,要求用户以任何方式自己处理这个问题的现状是把责任归咎于另一个人的门 IMO。

如果我是你,我会自己解决这个问题,或者让我的对象像 JSON 一样友好——例如让它们看起来像原生 Python 数据结构(JSON 是它的替代表示)。当然,更简单的方法是避免这种结构。

于 2013-01-15T01:21:24.890 回答