I'm having trouble getting Python's jsonpickle 0.4.0 to "recurse" in to custom objects that contain custom objects. Here's sample code that shows my problem.
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)
Balls have "color", Boxes have "contents". If I have a [native] array of Balls, then jsonpickle
works. If I have a Box of [native] ints, then jsonpickle
works.
But if I have a Box of Balls, jsonpickle
bombs with "Ball object is not JSON serializable"
.
From the stacktrace, I have the hunch that the encoder is leaving jsonpickle
and going off to some other JSON library... that apparently doesn't know that I've registered the BallHandler.
How can I fix this up?
By the way, my sample is NOT expressly using any part of Django, but I will be needing this to work in a Django app.
THANKS IN ADVANCE FOR ANY INPUT!
I think you can call back to the pickling context to continue the pickling.
class BoxHandler(jsonpickle.handlers.BaseHandler):
def flatten(self, obj, data):
return [self.context.flatten(x,reset=False) for x in obj.contents]
This seems to be similar to how the built in _list_recurse() function handles this case in pickler.py:44, as flatten() just calls self._flatten (after optionally resetting the state variables).
def _list_recurse(self, obj):
return [self._flatten(v) for v in obj]
I'm just testing on this now, and the _depth seems to be maintained as expected.