diff rhodecode/lib/ext_json.py @ 2173:775a7672d363 beta

add ext_json module
author Marcin Kuzminski <marcin@python-works.com>
date Wed, 28 Mar 2012 23:11:49 +0200
parents
children 85a64b981c07
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rhodecode/lib/ext_json.py	Wed Mar 28 23:11:49 2012 +0200
@@ -0,0 +1,104 @@
+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
+    import simplejson as _sj
+
+    def extended_encode(obj):
+        try:
+            return _obj_dump(obj)
+        except NotImplementedError:
+            pass
+        raise TypeError("%r is not JSON serializable" % (obj,))
+    # we handle decimals our own it makes unified behavior of json vs 
+    # simplejson
+    _sj.dumps = functools.partial(_sj.dumps, default=extended_encode,
+                                  use_decimal=False)
+    _sj.dump = functools.partial(_sj.dump, default=extended_encode,
+                                 use_decimal=False)
+    simplejson = _sj
+
+except ImportError:
+    # no simplejson set it to None
+    _sj = None
+
+
+# simplejson not found try out regular json module
+import json as _json
+
+
+# extended JSON encoder for json
+class ExtendedEncoder(_json.JSONEncoder):
+    def default(self, obj):
+        try:
+            return _obj_dump(obj)
+        except NotImplementedError:
+            pass
+        return _json.JSONEncoder.default(self, obj)
+# monkey-patch JSON encoder to use extended version
+_json.dumps = functools.partial(_json.dumps, cls=ExtendedEncoder)
+_json.dump = functools.partial(_json.dump, cls=ExtendedEncoder)
+stdlib = _json
+
+# set all available json modules
+simplejson = _sj
+stdjson = _json
+json = _sj if _sj else _json