ext_json.py
111 lines
| 3.1 KiB
| text/x-python
|
PythonLexer
r2173 | import datetime | |||
import functools | ||||
import decimal | ||||
__all__ = ['json', 'simplejson', 'stdjson'] | ||||
def _is_aware(value): | ||||
""" | ||||
Determines if a given datetime.time is aware. | ||||
The logic is described in Python's docs: | ||||
http://docs.python.org/library/datetime.html#datetime.tzinfo | ||||
""" | ||||
return (value.tzinfo is not None | ||||
and value.tzinfo.utcoffset(value) is not None) | ||||
def _obj_dump(obj): | ||||
""" | ||||
Custom function for dumping objects to JSON, if obj has __json__ attribute | ||||
or method defined it will be used for serialization | ||||
:param obj: | ||||
""" | ||||
if isinstance(obj, complex): | ||||
return [obj.real, obj.imag] | ||||
# See "Date Time String Format" in the ECMA-262 specification. | ||||
# some code borrowed from django 1.4 | ||||
elif isinstance(obj, datetime.datetime): | ||||
r = obj.isoformat() | ||||
if obj.microsecond: | ||||
r = r[:23] + r[26:] | ||||
if r.endswith('+00:00'): | ||||
r = r[:-6] + 'Z' | ||||
return r | ||||
elif isinstance(obj, datetime.date): | ||||
return obj.isoformat() | ||||
elif isinstance(obj, decimal.Decimal): | ||||
return str(obj) | ||||
elif isinstance(obj, datetime.time): | ||||
if _is_aware(obj): | ||||
raise ValueError("JSON can't represent timezone-aware times.") | ||||
r = obj.isoformat() | ||||
if obj.microsecond: | ||||
r = r[:12] | ||||
return r | ||||
elif isinstance(obj, set): | ||||
return list(obj) | ||||
elif hasattr(obj, '__json__'): | ||||
if callable(obj.__json__): | ||||
return obj.__json__() | ||||
else: | ||||
return obj.__json__ | ||||
else: | ||||
raise NotImplementedError | ||||
# Import simplejson | ||||
try: | ||||
# import simplejson initially | ||||
r2528 | import simplejson | |||
r2173 | ||||
def extended_encode(obj): | ||||
try: | ||||
return _obj_dump(obj) | ||||
except NotImplementedError: | ||||
pass | ||||
raise TypeError("%r is not JSON serializable" % (obj,)) | ||||
r2174 | # we handle decimals our own it makes unified behavior of json vs | |||
r2173 | # simplejson | |||
r2528 | simplejson.dumps = functools.partial(simplejson.dumps, | |||
default=extended_encode, | ||||
use_decimal=False) | ||||
simplejson.dump = functools.partial(simplejson.dump, | ||||
default=extended_encode, | ||||
use_decimal=False) | ||||
r2173 | except ImportError: | |||
# no simplejson set it to None | ||||
r2528 | simplejson = None | |||
r2173 | ||||
r2258 | try: | |||
# simplejson not found try out regular json module | ||||
r2528 | import json | |||
r2173 | ||||
r2258 | # extended JSON encoder for json | |||
r2528 | class ExtendedEncoder(json.JSONEncoder): | |||
r2258 | def default(self, obj): | |||
try: | ||||
return _obj_dump(obj) | ||||
except NotImplementedError: | ||||
pass | ||||
r2528 | return json.JSONEncoder.default(self, obj) | |||
r2258 | # monkey-patch JSON encoder to use extended version | |||
r2528 | json.dumps = functools.partial(json.dumps, cls=ExtendedEncoder) | |||
json.dump = functools.partial(json.dump, cls=ExtendedEncoder) | ||||
r2258 | except ImportError: | |||
r2528 | json = None | |||
stdlib = json | ||||
r2173 | ||||
# set all available json modules | ||||
r2528 | if simplejson: | |||
json = simplejson | ||||
elif json: | ||||
json = json | ||||
else: | ||||
raise ImportError('Could not find any json modules') | ||||