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 """Python library for serializing any arbitrary object graph into
10 `JSON <http://www.json.org/>`_. It can take almost any Python object and turn
11 the object into JSON. Additionally, it can reconstitute the object back into
15 >>> from jsonpickle.tests.classes import Thing
19 >>> obj = Thing('A String')
23 Use jsonpickle to transform the object into a JSON string.
25 >>> pickled = jsonpickle.encode(obj)
27 {"py/object": "jsonpickle.tests.classes.Thing", "name": "A String", "child": null}
29 Use jsonpickle to recreate a Python object from a JSON string
31 >>> unpickled = jsonpickle.decode(pickled)
32 >>> str(unpickled.name)
37 Loading a JSON string from an untrusted source represents a potential
38 security vulnerability. jsonpickle makes no attempt to sanitize the input.
40 The new object has the same type and data, but essentially is now a copy of
45 >>> obj.name == unpickled.name
47 >>> type(obj) == type(unpickled)
50 If you will never need to load (regenerate the Python class from JSON), you can
51 pass in the keyword unpicklable=False to prevent extra information from being
54 >>> oneway = jsonpickle.encode(obj, unpicklable=False)
56 {"name": "A String", "child": null}
60 from jsonpickle
.pickler
import Pickler
61 from jsonpickle
.unpickler
import Unpickler
64 __all__
= ('encode', 'decode')
67 class JSONPluginMgr(object):
68 """The JSONPluginMgr handles encoding and decoding.
70 It tries these modules in this order:
71 cjson, json, simplejson, demjson
73 cjson is the fastest, and is tried first.
74 json comes with python2.6 and is tried second.
75 simplejson is a very popular backend and is tried third.
76 demjson is the most permissive backend and is tried last.
79 ## The names of backends that have been successfully imported
80 self
._backend
_names
= []
82 ## A dictionary mapping backend names to encode/decode functions
86 ## Options to pass to specific encoders
87 self
._encoder
_options
= {}
89 ## The exception class that is thrown when a decoding error occurs
90 self
._decoder
_exceptions
= {}
92 ## Whether we've loaded any backends successfully
93 self
._verified
= False
95 ## Try loading cjson, simplejson and demjson
96 self
.load_backend('cjson', 'encode', 'decode', 'DecodeError')
97 self
.load_backend('json', 'dumps', 'loads', ValueError)
98 self
.load_backend('simplejson', 'dumps', 'loads', ValueError)
99 self
.load_backend('demjson', 'encode', 'decode', 'JSONDecodeError')
102 """Ensures that we've loaded at least one JSON backend.
106 raise AssertionError('jsonpickle requires at least one of the '
108 ' cjson, python2.6, simplejson, or demjson')
110 def load_backend(self
, name
, encode_name
, decode_name
, decode_exc
):
111 """Loads a backend by name.
113 This method loads a backend and sets up references to that
114 backend's encode/decode functions and exception classes.
116 encode_name is the name of the backend's encode method.
117 The method should take an object and return a string.
119 decode_name names the backend's method for the reverse
120 operation -- returning a Python object from a string.
122 decode_exc can be either the name of the exception class
123 used to denote decoding errors, or it can be a direct reference
124 to the appropriate exception class itself. If it is a name,
125 then the assumption is that an exception class of that name
126 can be found in the backend module's namespace.
129 ## Load the JSON backend
130 mod
= __import__(name
)
135 ## Handle submodules, e.g. django.utils.simplejson
136 components
= name
.split('.')
137 for comp
in components
[1:]:
138 mod
= getattr(mod
, comp
)
139 except AttributeError:
143 ## Setup the backend's encode/decode methods
144 self
._encoders
[name
] = getattr(mod
, encode_name
)
145 self
._decoders
[name
] = getattr(mod
, decode_name
)
146 except AttributeError:
147 self
.remove_backend(name
)
151 if type(decode_exc
) is str:
152 ## This backend's decoder exception is part of the backend
153 self
._decoder
_exceptions
[name
] = getattr(mod
, decode_exc
)
155 ## simplejson uses the ValueError exception
156 self
._decoder
_exceptions
[name
] = decode_exc
157 except AttributeError:
158 self
.remove_backend(name
)
161 ## Setup the default args and kwargs for this encoder
162 self
._encoder
_options
[name
] = ([], {})
164 ## Add this backend to the list of candidate backends
165 self
._backend
_names
.append(name
)
167 ## Indicate that we successfully loaded a JSON backend
168 self
._verified
= True
170 def remove_backend(self
, name
):
171 """Removes all entries for a particular backend.
173 self
._encoders
.pop(name
, None)
174 self
._decoders
.pop(name
, None)
175 self
._decoder
_exceptions
.pop(name
, None)
176 self
._encoder
_options
.pop(name
, None)
177 if name
in self
._backend
_names
:
178 self
._backend
_names
.remove(name
)
179 self
._verified
= bool(self
._backend
_names
)
181 def encode(self
, obj
):
182 """Attempts to encode an object into JSON.
184 This tries the loaded backends in order and passes along the last
185 exception if no backend is able to encode the object.
188 for idx
, name
in enumerate(self
._backend
_names
):
190 optargs
, kwargs
= self
._encoder
_options
[name
]
191 args
= (obj
,) + tuple(optargs
)
192 return self
._encoders
[name
](*args
, **kwargs
)
194 if idx
== len(self
._backend
_names
) - 1:
197 def decode(self
, string
):
198 """Attempts to decode an object from a JSON string.
200 This tries the loaded backends in order and passes along the last
201 exception if no backends are able to decode the string.
204 for idx
, name
in enumerate(self
._backend
_names
):
206 return self
._decoders
[name
](string
)
207 except self
._decoder
_exceptions
[name
], e
:
208 if idx
== len(self
._backend
_names
) - 1:
211 pass # and try a more forgiving encoder, e.g. demjson
213 def set_preferred_backend(self
, name
):
214 """Sets the preferred json backend.
216 If a preferred backend is set then jsonpickle tries to use it
217 before any other backend.
220 set_preferred_backend('simplejson')
222 If the backend is not one of the built-in jsonpickle backends
223 (cjson, json/simplejson, or demjson) then you must load the
224 backend prior to calling set_preferred_backend. An AssertionError
225 exception is raised if the backend has not been loaded.
227 if name
in self
._backend
_names
:
228 self
._backend
_names
.remove(name
)
229 self
._backend
_names
.insert(0, name
)
231 errmsg
= 'The "%s" backend has not been loaded.' % name
232 raise AssertionError(errmsg
)
234 def set_encoder_options(self
, name
, *args
, **kwargs
):
235 """Associates encoder-specific options with an encoder.
237 After calling set_encoder_options, any calls to jsonpickle's
238 encode method will pass the supplied args and kwargs along to
239 the appropriate backend's encode method.
242 set_encoder_options('simplejson', sort_keys=True, indent=4)
243 set_encoder_options('demjson', compactly=False)
245 See the appropriate encoder's documentation for details about
246 the supported arguments and keyword arguments.
248 self
._encoder
_options
[name
] = (args
, kwargs
)
250 # Initialize a JSONPluginMgr
251 json
= JSONPluginMgr()
253 # Export specific JSONPluginMgr methods into the jsonpickle namespace
254 set_preferred_backend
= json
.set_preferred_backend
255 set_encoder_options
= json
.set_encoder_options
256 load_backend
= json
.load_backend
257 remove_backend
= json
.remove_backend
260 def encode(value
, unpicklable
=True, max_depth
=None, **kwargs
):
261 """Returns a JSON formatted representation of value, a Python object.
263 The keyword argument 'unpicklable' defaults to True.
264 If set to False, the output will not contain the information
265 necessary to turn the JSON data back into Python objects.
267 The keyword argument 'max_depth' defaults to None.
268 If set to a non-negative integer then jsonpickle will not recurse
269 deeper than 'max_depth' steps into the object. Anything deeper
270 than 'max_depth' is represented using a Python repr() of the object.
272 >>> encode('my string')
277 >>> encode({'foo': True})
280 >>> encode({'foo': True}, max_depth=0)
281 '"{\\'foo\\': True}"'
283 >>> encode({'foo': True}, max_depth=1)
288 j
= Pickler(unpicklable
=unpicklable
,
290 return json
.encode(j
.flatten(value
))
293 """Converts the JSON string into a Python object.
295 >>> str(decode('"my string"'))
301 return j
.restore(json
.decode(string
))