Skip to content

Commit 263281d

Browse files
committed
Fix issue encode#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
1 parent 134ffd9 commit 263281d

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

rest_framework/tests/test_renderers.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
import datetime
1919
import pickle
2020
import re
21+
import UserDict
22+
import collections
23+
import json
2124

2225

2326
DUMMYSTATUS = status.HTTP_200_OK
@@ -244,6 +247,60 @@ def test_render_lazy_strings(self):
244247
ret = JSONRenderer().render(_('test'))
245248
self.assertEqual(ret, b'"test"')
246249

250+
def test_render_userdict_obj(self):
251+
class DictLike(UserDict.DictMixin):
252+
def __init__(self):
253+
self._dict = dict()
254+
def __getitem__(self, key):
255+
return self._dict.__getitem__(key)
256+
def __setitem__(self, key, value):
257+
return self._dict.__setitem__(key, value)
258+
def __delitem__(self, key):
259+
return self._dict.__delitem__(key)
260+
def keys(self):
261+
return self._dict.keys()
262+
x = DictLike()
263+
x['a'] = 1
264+
x['b'] = "string value"
265+
ret = JSONRenderer().render(x)
266+
self.assertEquals(json.loads(ret), {u'a': 1, u'b': u'string value'})
267+
268+
def test_render_dict_abc_obj(self):
269+
class Dict(collections.MutableMapping):
270+
def __init__(self):
271+
self._dict = dict()
272+
def __getitem__(self, key):
273+
return self._dict.__getitem__(key)
274+
def __setitem__(self, key, value):
275+
return self._dict.__setitem__(key, value)
276+
def __delitem__(self, key):
277+
return self._dict.__delitem__(key)
278+
def __iter__(self):
279+
return self._dict.__iter__()
280+
def __len__(self):
281+
return self._dict.__len__()
282+
283+
x = Dict()
284+
x['key'] = 'string value'
285+
x[2] = 3
286+
ret = JSONRenderer().render(x)
287+
self.assertEquals(json.loads(ret), {u'key': 'string value', u'2': 3})
288+
289+
290+
def test_render_obj_with_getitem(self):
291+
class DictLike(object):
292+
def __init__(self):
293+
self._dict = {}
294+
def set(self, value):
295+
self._dict = dict(value)
296+
def __getitem__(self, key):
297+
return self._dict[key]
298+
299+
x = DictLike()
300+
x.set({'a': 1, 'b': 'string'})
301+
with self.assertRaises(TypeError):
302+
JSONRenderer().render(x)
303+
247304
def test_without_content_type_args(self):
248305
"""
249306
Test basic JSON rendering.

rest_framework/utils/encoders.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ def default(self, o):
4444
return str(o)
4545
elif hasattr(o, 'tolist'):
4646
return o.tolist()
47+
elif hasattr(o, '__getitem__'):
48+
try:
49+
return dict(o)
50+
except KeyError:
51+
# Couldn't convert to a dict, fall through
52+
pass
4753
elif hasattr(o, '__iter__'):
4854
return [i for i in o]
4955
return super(JSONEncoder, self).default(o)

0 commit comments

Comments
 (0)