Source

jaraco.json / jaraco / json / main.py

Full commit
"""
A JSON encoder and decoder with hooks for serializing basic objects.

Native types should be encoded naturally.

>>> encode('foo')
'"foo"'
>>> encode(15)
'15'
>>> encode({'mykey': 'myval'})
'{"mykey": "myval"}'

Also, custom objects can be encoded.
>>> MyCustom = type('MyCustom', (), dict()) # an simple class
>>> encode(MyCustom())
'{"__python_module__": "jaraco.json.main", "state": "{}", "args": "[]", "__python_class__": "MyCustom"}'

"""

from __future__ import absolute_import
# http://docs.python.org/library/json.html
import json

class GenericEncoder(json.JSONEncoder):
	"""
	A JSON encoder that encodes any Python object similar to how
	the pickle module works.
	"""
	def default(self, object):
		# use the pickle protocol 2 to serialize the object
		reduced = object.__reduce_ex__(2)
		return self.save_reduce(*reduced, obj = object)

	def save_reduce(self, func, args, state, listitems=None,
		dictitems=None, obj=None):
		"""
		generate a JSON representation of this object's reduce_ex
		result.
		"""
		cls, args = args[0], args[1:]
		return dict(
			__python_class__ = cls.__name__,
			__python_module__ = cls.__module__,
			args = self.encode(args),
			state = self.encode(state),
			)

encode = GenericEncoder().encode

def _find_module_by_name(mod_name):
	mod_sep = '.'
	parent = __import__(mod_name)
	parent_name, sep, children = mod_name.partition(mod_sep)
	if not children: return parent
	for child in children.split(mod_sep):
		parent = getattr(parent, child)
	return parent

def decode_object_hook(object):
	if '__python_class__' not in object.keys():
		return object
	class_name = object['__python_class__']
	mod_name = object['__python_module__']
	args = decode(object['args'])
	state = decode(object['state'])
	mod = _find_module_by_name(mod_name)
	cls = getattr(mod, class_name)
	ob = cls.__new__(cls, *args)
	ob.__dict__.update(state)
	return ob

decode = json.JSONDecoder(object_hook = decode_object_hook).decode