Fix issue #1231: JSONEncoder doesn't handle dict-like objects

Check for __getitem__ and then attempt to convert to a dict.
The check for __getitem__ is there as there's no universal way to
check if an object is a mapping type, but this is a likely proxy
This commit is contained in:
Malcolm Box 2013-11-21 20:09:48 +00:00
parent 134ffd96a1
commit 263281d71d
2 changed files with 63 additions and 0 deletions

View File

@ -18,6 +18,9 @@ from rest_framework.test import APIRequestFactory
import datetime
import pickle
import re
import UserDict
import collections
import json
DUMMYSTATUS = status.HTTP_200_OK
@ -244,6 +247,60 @@ class JSONRendererTests(TestCase):
ret = JSONRenderer().render(_('test'))
self.assertEqual(ret, b'"test"')
def test_render_userdict_obj(self):
class DictLike(UserDict.DictMixin):
def __init__(self):
self._dict = dict()
def __getitem__(self, key):
return self._dict.__getitem__(key)
def __setitem__(self, key, value):
return self._dict.__setitem__(key, value)
def __delitem__(self, key):
return self._dict.__delitem__(key)
def keys(self):
return self._dict.keys()
x = DictLike()
x['a'] = 1
x['b'] = "string value"
ret = JSONRenderer().render(x)
self.assertEquals(json.loads(ret), {u'a': 1, u'b': u'string value'})
def test_render_dict_abc_obj(self):
class Dict(collections.MutableMapping):
def __init__(self):
self._dict = dict()
def __getitem__(self, key):
return self._dict.__getitem__(key)
def __setitem__(self, key, value):
return self._dict.__setitem__(key, value)
def __delitem__(self, key):
return self._dict.__delitem__(key)
def __iter__(self):
return self._dict.__iter__()
def __len__(self):
return self._dict.__len__()
x = Dict()
x['key'] = 'string value'
x[2] = 3
ret = JSONRenderer().render(x)
self.assertEquals(json.loads(ret), {u'key': 'string value', u'2': 3})
def test_render_obj_with_getitem(self):
class DictLike(object):
def __init__(self):
self._dict = {}
def set(self, value):
self._dict = dict(value)
def __getitem__(self, key):
return self._dict[key]
x = DictLike()
x.set({'a': 1, 'b': 'string'})
with self.assertRaises(TypeError):
JSONRenderer().render(x)
def test_without_content_type_args(self):
"""
Test basic JSON rendering.

View File

@ -44,6 +44,12 @@ class JSONEncoder(json.JSONEncoder):
return str(o)
elif hasattr(o, 'tolist'):
return o.tolist()
elif hasattr(o, '__getitem__'):
try:
return dict(o)
except KeyError:
# Couldn't convert to a dict, fall through
pass
elif hasattr(o, '__iter__'):
return [i for i in o]
return super(JSONEncoder, self).default(o)