Skip to content

Instantly share code, notes, and snippets.

@sunu
Created June 21, 2013 05:51
Show Gist options
  • Save sunu/5829161 to your computer and use it in GitHub Desktop.
Save sunu/5829161 to your computer and use it in GitHub Desktop.
diff --git a/lib/python2.7/site-packages/json_field/fields1.py b/json_field/fields.py
index dbf1c9d..f14d5ea 100644
--- a/lib/python2.7/site-packages/json_field/fields1.py
+++ b/json_field/fields.py
@@ -24,6 +24,55 @@ TIME_RE = re.compile(r'^\d{2}:\d{2}:\d{2}')
DATE_RE = re.compile(r'^\d{4}-\d{2}-\d{2}(?!T)')
DATETIME_RE = re.compile(r'^\d{4}-\d{2}-\d{2}T')
+
+class Converter():
+ """A class containing different encoder/decoder functions."""
+
+ @classmethod
+ def encode(cls, obj):
+ """A custom encoder to encode both built-in and custom objects into
+ JSON-serializable python objects recursively.
+
+ Custom objects are encoded as dict with one key, '__TypeName__' where
+ 'TypeName' is the actual name of the class to which the object belongs.
+ That single key maps to another dict which is just the encoded __dict__
+ of the object being encoded."""
+
+ if isinstance(
+ obj, (int, long, float, complex, bool, basestring, type(None))
+ ):
+ return obj
+ elif isinstance(obj, list):
+ return [cls.encode(item) for item in obj]
+ elif isinstance(obj, (set, tuple, complex)):
+ raise NotImplementedError
+ elif isinstance(obj, dict):
+ result = {}
+ for key in obj:
+ # Every Model instance in django has a ModelState object,
+ # which we don't want to be serialized. Hence, we get rid
+ # of it.
+ if key != '__ModelState__':
+ result[key] = cls.encode(obj[key])
+ return result
+ else:
+ obj_dict = cls.encode(obj.__dict__)
+ return {'__%s__' % (obj.__class__.__name__): obj_dict}
+
+ @classmethod
+ def decode(cls, obj_class, value):
+ object_list = []
+ arg_list = obj_class.attr_list
+ for instance in value:
+ arg_dict = {}
+ for arg in arg_list:
+ arg_dict[arg] = instance['__%s__' % (
+ obj_class.__name__)].get(arg)
+ obj = obj_class(**arg_dict)
+ object_list.append(obj)
+ return object_list
+
+
class JSONDecoder(json.JSONDecoder):
""" Recursive JSON to Python deserialization. """
@@ -107,6 +156,7 @@ class JSONField(models.TextField):
self._db_type = kwargs.pop('db_type', None)
self.evaluate_formfield = kwargs.pop('evaluate_formfield', False)
+ self.schema = kwargs.pop('schema', {})
encoder = kwargs.pop('encoder', DjangoJSONEncoder)
decoder = kwargs.pop('decoder', JSONDecoder)
encoder_kwargs = kwargs.pop('encoder_kwargs', {})
@@ -129,11 +179,15 @@ class JSONField(models.TextField):
return super(JSONField, self).db_type(*args, **kwargs)
def to_python(self, value):
- if value is None: # allow blank objects
+ if value is None: # allow blank objects
return None
- if isinstance(value, basestring):
+ elif isinstance(value, basestring):
try:
value = json.loads(value, **self.decoder_kwargs)
+ schema = self.schema
+ if isinstance(schema, list) and isinstance(schema[0], object):
+ obj_class = schema[0]
+ value = Converter.decode(obj_class, value)
except JSON_DECODE_ERROR:
pass
return value
@@ -141,6 +195,20 @@ class JSONField(models.TextField):
def get_db_prep_value(self, value, *args, **kwargs):
if self.null and value is None and not kwargs.get('force'):
return None
+ elif isinstance(value, (list, dict)):
+ schema = self.schema
+ assert type(schema) == type(value)
+ if isinstance(value, list):
+ for val in value:
+ assert isinstance(val, schema[0])
+ elif isinstance(value, dict):
+ for key, val in value:
+ assert key in schema.keys()
+ assert isinstance(val, schema[key])
+ else:
+ raise ValueError
+ return_value = Converter.encode(value)
+ return json.dumps(return_value, **self.encoder_kwargs)
return json.dumps(value, **self.encoder_kwargs)
def value_to_string(self, obj):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment