我无法让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)
球有“颜色”,盒子有“内容”。如果我有一个[本机]球阵列,那么jsonpickle
可以正常工作。如果我有一盒[native] int,那么jsonpickle
就可以了。
但如果我有一盒球,jsonpickle
炸弹"Ball object is not JSON serializable"
。
从堆栈跟踪中,我预感到编码器正在离开jsonpickle
并转到其他一些JSON库...显然我不知道我已经注册了BallHandler。
我该如何解决这个问题?
顺便说一下,我的示例并没有明确地使用Django的任何部分,但我需要这个才能在Django应用程序中工作。
感谢任何输入!
答案 0 :(得分:4)
首先,您为什么要首先创建自定义处理程序?您尝试执行与默认处理程序已完成相同的操作。移除这两条register
行并使用encode
和所有这些对象调用unpicklable=False
,您将获得相同的结果 - 除了它将按照您想要的方式工作充满了球,而不是失败。
如果您查看教程,API,测试用例和示例,他们永远不会创建自定义处理程序来模拟这样的集合。 (例如,查看测试套件中的Node
/ Document
/ Section
类(samples.py
和document_test.py
)。)所以,我认为你正试图做一些你不应该做的事情,并不打算做。
但是,让我们看看你的实际问题:为什么它不起作用?
嗯,那个很容易。你这样做是错的。根据{{3}}的文档,您应该:将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
- 而且我没有看到你能做到的任何方式 - 这可能会违反maxdepth
和unpicklable
的{{1}}参数。
所以,也许 没有正确的方法来做到这一点。
答案 1 :(得分:2)
对我而言似乎是一个错误,也是一个原则。如果jsonpickle
是关于向json
添加自定义对象处理,则它应该集成到后者中,而不是尝试为其“预处理”内容。要求用户以任何方式as abarnert said以自己的方式处理此问题的当前状态正在归咎于另一个人的IMO门。
如果我是你,我会自己解决这个问题,或者让我的对象JSON友好 - 例如使它们看起来像本机Python数据结构(JSON是其替代表示)。当然,更简单的方法是避免使用这种结构,这是一种kludge。
答案 2 :(得分:2)
我认为你可以回调酸洗环境继续酸洗。
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似乎按预期进行了维护。