1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2008 John Paulett (john -at- 7oars.com)
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution.
9 import jsonpickle
.util
as util
10 import jsonpickle
.tags
as tags
13 class Pickler(object):
14 """Converts a Python object to a JSON representation.
16 Setting unpicklable to False removes the ability to regenerate
17 the objects into object types beyond what the standard
18 simplejson library supports.
20 Setting max_depth to a negative number means there is no
21 limit to the depth jsonpickle should recurse into an
22 object. Setting it to zero or higher places a hard limit
23 on how deep jsonpickle recurses into objects, dictionaries, etc.
26 >>> p.flatten('hello world')
30 def __init__(self
, unpicklable
=True, max_depth
=None):
31 self
.unpicklable
= unpicklable
32 ## The current recursion depth
34 ## The maximal recursion depth
35 self
._max
_depth
= max_depth
36 ## Maps id(obj) to reference names
38 ## The namestack grows whenever we recurse into a child object
46 """Steps down one level in the namespace.
50 def _pop(self
, value
):
51 """Step up one level in the namespace and return the value.
52 If we're at the root, reset the pickler's state.
59 def _mkref(self
, obj
):
61 if objid
not in self
._objs
:
62 self
._objs
[objid
] = '/' + '/'.join(self
._namestack
)
66 def _getref(self
, obj
):
67 return {tags
.REF
: self
._objs
.get(id(obj
))}
69 def flatten(self
, obj
):
70 """Takes an object and returns a JSON-safe representation of it.
72 Simply returns any of the basic builtin datatypes
75 >>> p.flatten('hello world')
77 >>> p.flatten(u'hello world')
87 >>> r = p.flatten(None)
92 >>> p.flatten([1, 2, 3, 4])
94 >>> p.flatten((1,2,))[tags.TUPLE]
96 >>> p.flatten({'key': 'value'})
102 if self
._depth
== self
._max
_depth
:
103 return self
._pop
(repr(obj
))
105 if util
.is_primitive(obj
):
106 return self
._pop
(obj
)
108 if util
.is_list(obj
):
109 return self
._pop
([ self
.flatten(v
) for v
in obj
])
111 # We handle tuples and sets by encoding them in a "(tuple|set)dict"
112 if util
.is_tuple(obj
):
113 return self
._pop
({tags
.TUPLE
: [ self
.flatten(v
) for v
in obj
]})
116 return self
._pop
({tags
.SET
: [ self
.flatten(v
) for v
in obj
]})
118 if util
.is_dictionary(obj
):
119 return self
._pop
(self
._flatten
_dict
_obj
(obj
, obj
.__class
__()))
121 if util
.is_type(obj
):
122 return self
._pop
(_mktyperef(obj
))
124 if util
.is_object(obj
):
126 has_class
= hasattr(obj
, '__class__')
127 has_dict
= hasattr(obj
, '__dict__')
129 if has_class
and not util
.is_repr(obj
):
130 module
, name
= _getclassdetail(obj
)
131 if self
.unpicklable
is True:
132 data
[tags
.OBJECT
] = '%s.%s' % (module
, name
)
134 if util
.is_repr(obj
):
135 if self
.unpicklable
is True:
136 data
[tags
.REPR
] = '%s/%s' % (obj
.__class
__.__module
__,
140 return self
._pop
(data
)
142 if util
.is_dictionary_subclass(obj
):
143 return self
._pop
(self
._flatten
_dict
_obj
(obj
, data
))
145 if util
.is_noncomplex(obj
):
146 return self
._pop
([self
.flatten(v
) for v
in obj
])
149 return self
._pop
(self
._flatten
_dict
_obj
(obj
.__dict
__, data
))
151 # We've seen this object before so place an object
152 # reference tag in the data. This avoids infinite recursion
153 # when processing cyclical objects.
154 return self
._pop
(self
._getref
(obj
))
156 return self
._pop
(data
)
157 # else, what else? (methods, functions, old style classes...)
159 def _flatten_dict_obj(self
, obj
, data
):
160 """Recursively call flatten() and return json-friendly dict
162 for k
, v
in obj
.iteritems():
163 if util
.is_function(v
):
165 if type(k
) not in types
.StringTypes
:
170 self
._namestack
.append(k
)
171 data
[k
] = self
.flatten(v
)
172 self
._namestack
.pop()
176 """Return a typeref dictionary. Used for references.
178 >>> from jsonpickle import tags
179 >>> _mktyperef(AssertionError)[tags.TYPE].rsplit('.', 1)[0]
182 >>> _mktyperef(AssertionError)[tags.TYPE].rsplit('.', 1)[-1]
185 return {tags
.TYPE
: '%s.%s' % (obj
.__module
__, obj
.__name
__)}
187 def _getclassdetail(obj
):
188 """Helper class to return the class of an object.
190 >>> class Example(object): pass
191 >>> _getclassdetail(Example())
192 ('jsonpickle.pickler', 'Example')
193 >>> _getclassdetail(25)
194 ('__builtin__', 'int')
195 >>> _getclassdetail(None)
196 ('__builtin__', 'NoneType')
197 >>> _getclassdetail(False)
198 ('__builtin__', 'bool')
201 module
= getattr(cls
, '__module__')
202 name
= getattr(cls
, '__name__')