#!/usr/bin/python
"""A tiny but complete JSON serializer."""

def json_encode(obj):
    for category, function in encoders:
        if isinstance(obj, category): return function(obj)
    raise JsonEncodingError(obj)

class JsonEncodingError(Exception): pass

def encode_unicode(obj):
    return '"%s"' % ''.join((char if ' ' <= char <= '~' and char not in '"\\'
                             else '\\u%04x' % ord(char))
                            for char in obj)

def encode_dict(obj):
    return '{%s}' % ', '.join('%s: %s' %
                              (json_encode(unicode(k)), json_encode(v))
                              for k, v in obj.items())

def encode_list(obj):
    return '[%s]' % ', '.join(json_encode(item) for item in obj)

encode_str  = lambda obj: json_encode(obj.decode('utf-8'))
encode_bool = lambda obj: 'true' if obj else 'false'
encode_none = lambda obj: 'null'

# `bool` must be registered before `int`, because `bool` is a subclass of `int`.
encoders = [ (str,        encode_str),  (bool,    encode_bool),
             (int,        str),         (float,   str),
             (type(None), encode_none), (unicode, encode_unicode),
             (dict,       encode_dict), (list,    encode_list)     ]

def ok(a, b): assert a == b, (a, b)
ok(json_encode([1, 2.5, {'x': u'\uabcd', 'yz': None}, False, True]),
             u'[1, 2.5, {"x": "\\uabcd", "yz": null}, false, true]')
