From bcf6f92dc50c1f23ca327c5dae4d616cfe212625 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Wed, 27 May 2009 09:58:34 +0000 Subject: * Fix-up a TODO (support the sort_key option). * Fix an error where True/False were being written-out as title-cased strings when used a dictionary keys. * Speed-up iteration over dicts by looping over items() rather than keys() followed by value lookups. * TODO: sort only by keys, not keys and values. --- Lib/json/encoder.py | 2 +- Lib/json/tests/test_encode_basestring_ascii.py | 5 +++ Modules/_json.c | 60 +++++++++++++++++--------- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py index 3c306ab..2beb10e 100644 --- a/Lib/json/encoder.py +++ b/Lib/json/encoder.py @@ -233,7 +233,7 @@ class JSONEncoder(object): if (_one_shot and c_make_encoder is not None - and not self.indent and not self.sort_keys): + and not self.indent): _iterencode = c_make_encoder( markers, self.default, _encoder, self.indent, self.key_separator, self.item_separator, self.sort_keys, diff --git a/Lib/json/tests/test_encode_basestring_ascii.py b/Lib/json/tests/test_encode_basestring_ascii.py index 8711469..dbcb09a 100644 --- a/Lib/json/tests/test_encode_basestring_ascii.py +++ b/Lib/json/tests/test_encode_basestring_ascii.py @@ -43,3 +43,8 @@ class TestEncodeBaseStringAscii(TestCase): items = [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)] s = json.dumps(OrderedDict(items)) self.assertEqual(s, '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}') + + def test_sorted_dict(self): + items = [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)] + s = json.dumps(dict(items), sort_keys=True) + self.assertEqual(s, '{"five": 5, "four": 4, "one": 1, "three": 3, "two": 2}') diff --git a/Modules/_json.c b/Modules/_json.c index 90f5736..f33503b 100644 --- a/Modules/_json.c +++ b/Modules/_json.c @@ -1334,9 +1334,9 @@ encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ss static PyObject *empty_dict = NULL; PyObject *kstr = NULL; PyObject *ident = NULL; - PyObject *key = NULL; - PyObject *value = NULL; PyObject *it = NULL; + PyObject *items; + PyObject *item = NULL; int skipkeys; Py_ssize_t idx; @@ -1379,16 +1379,38 @@ encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ss */ } - /* TODO: C speedup not implemented for sort_keys */ + items = PyObject_CallMethod(dct, "items", ""); /* XXX key=itemgetter(0) */ + if (items == NULL) + goto bail; + if (PyObject_IsTrue(s->sort_keys)) { + PyObject *rv; + PyObject *itemlist; + + itemlist = PySequence_List(items); + Py_DECREF(items); + if (itemlist == NULL) + goto bail; - it = PyObject_GetIter(dct); - if (it == NULL) + rv = PyObject_CallMethod(itemlist, "sort", ""); + if (rv == NULL) { + Py_DECREF(itemlist); + goto bail; + } + items = itemlist; + } + it = PyObject_GetIter(items); + Py_DECREF(items); + if (it == NULL) goto bail; skipkeys = PyObject_IsTrue(s->skipkeys); idx = 0; - while ((key = PyIter_Next(it)) != NULL) { - PyObject *encoded; - + while ((item = PyIter_Next(it)) != NULL) { + PyObject *encoded, *key, *value; + if (!PyTuple_Check(item) || Py_SIZE(item) != 2) { + PyErr_SetString(PyExc_ValueError, "items must return 2-tuples"); + goto bail; + } + key = PyTuple_GET_ITEM(item, 0); if (PyUnicode_Check(key)) { Py_INCREF(key); kstr = key; @@ -1398,18 +1420,20 @@ encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ss if (kstr == NULL) goto bail; } - else if (PyLong_Check(key)) { - kstr = PyObject_Str(key); + else if (key == Py_True || key == Py_False || key == Py_None) { + /* This must come before the PyLong_Check because + True and False are also 1 and 0.*/ + kstr = _encoded_const(key); if (kstr == NULL) goto bail; } - else if (key == Py_True || key == Py_False || key == Py_None) { - kstr = _encoded_const(key); + else if (PyLong_Check(key)) { + kstr = PyObject_Str(key); if (kstr == NULL) goto bail; } else if (skipkeys) { - Py_DECREF(key); + Py_DECREF(item); continue; } else { @@ -1435,14 +1459,11 @@ encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ss if (PyList_Append(rval, s->key_separator)) goto bail; - value = PyObject_GetItem(dct, key); - if (value == NULL) - goto bail; + value = PyTuple_GET_ITEM(item, 1); if (encoder_listencode_obj(s, rval, value, indent_level)) goto bail; idx += 1; - Py_CLEAR(value); - Py_DECREF(key); + Py_DECREF(item); } if (PyErr_Occurred()) goto bail; @@ -1466,8 +1487,7 @@ encoder_listencode_dict(PyEncoderObject *s, PyObject *rval, PyObject *dct, Py_ss bail: Py_XDECREF(it); - Py_XDECREF(key); - Py_XDECREF(value); + Py_XDECREF(item); Py_XDECREF(kstr); Py_XDECREF(ident); return -1; -- cgit v0.12