aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalcolm Box2013-11-21 20:09:48 +0000
committerMalcolm Box2013-11-21 20:09:48 +0000
commit263281d71d0425d7bb9b4ebbdf1811ef637ee60a (patch)
tree9c14f0bc14a2229b9aadd3c20a9131a476d07cfd
parent134ffd96a14559f3a1ff4e5210c8718fb4bff9b1 (diff)
downloaddjango-rest-framework-263281d71d0425d7bb9b4ebbdf1811ef637ee60a.tar.bz2
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
-rw-r--r--rest_framework/tests/test_renderers.py57
-rw-r--r--rest_framework/utils/encoders.py6
2 files changed, 63 insertions, 0 deletions
diff --git a/rest_framework/tests/test_renderers.py b/rest_framework/tests/test_renderers.py
index 76299a89..18da6ef8 100644
--- a/rest_framework/tests/test_renderers.py
+++ b/rest_framework/tests/test_renderers.py
@@ -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.
diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py
index 35ad206b..22b1ab3d 100644
--- a/rest_framework/utils/encoders.py
+++ b/rest_framework/utils/encoders.py
@@ -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)