diff -r 6d278f426417 Lib/json/encoder.py --- a/Lib/json/encoder.py Fri Jul 05 18:05:29 2013 -1000 +++ b/Lib/json/encoder.py Sun Jul 07 09:51:25 2013 +0200 @@ -1,6 +1,7 @@ """Implementation of JSONEncoder """ import re +import decimal try: from _json import encode_basestring_ascii as c_encode_basestring_ascii @@ -260,6 +261,7 @@ list=list, str=str, tuple=tuple, + Decimal=decimal.Decimal, ): if _indent is not None and not isinstance(_indent, str): @@ -301,6 +303,8 @@ yield buf + str(value) elif isinstance(value, float): yield buf + _floatstr(value) + elif isinstance(value, Decimal): + yield buf + str(value) else: yield buf if isinstance(value, (list, tuple)): @@ -377,6 +381,8 @@ yield str(value) elif isinstance(value, float): yield _floatstr(value) + elif isinstance(value, decimal.Decimal): + yield str(value) else: if isinstance(value, (list, tuple)): chunks = _iterencode_list(value, _current_indent_level) @@ -405,6 +411,8 @@ yield str(o) elif isinstance(o, float): yield _floatstr(o) + elif isinstance(o, decimal.Decimal): + yield str(o) elif isinstance(o, (list, tuple)): yield from _iterencode_list(o, _current_indent_level) elif isinstance(o, dict): diff -r 6d278f426417 Lib/test/json_tests/test_decimal.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/json_tests/test_decimal.py Sun Jul 07 09:51:25 2013 +0200 @@ -0,0 +1,34 @@ +""" +Unittest module to check for correct handling of Decimals in JSON encoding +""" +import math, json +from decimal import Decimal +from test.json_tests import PyTest, CTest + + +class TestDecimal: + """Test that Decimals are correctly encoded into JSON""" + def test_decimals(self): + """Check for a few Decimal to be rendered correctly into JSON""" + for num in [Decimal('1617161771.7650001'), Decimal(str(math.pi)), + Decimal('1'), Decimal('-55555555555555555555.555555555')]: + self.assertEqual(Decimal(self.dumps(num)), num) + self.assertEqual(self.loads(self.dumps(num), parse_float=Decimal), + num) + + def test_decimal_in_a_list(self): + """Check that a Decimal within a list is handled properly""" + l = [Decimal('0.2')] + self.assertEqual(json.dumps(l), '[0.2]') + self.assertEqual(self.loads(self.dumps(l), parse_float=Decimal), l) + + def test_decimal_in_a_dict(self): + """Check that a Decimal within a dict (as key and value) + is handled properly. + """ + d = {Decimal('4.4'): Decimal('0.2')} + self.assertEqual(json.dumps(d), '{"4.4": 0.2}') + + +class TestPyFloat(TestDecimal, PyTest): pass +class TestCFloat(TestDecimal, CTest): pass diff -r 6d278f426417 Modules/_json.c --- a/Modules/_json.c Fri Jul 05 18:05:29 2013 -1000 +++ b/Modules/_json.c Sun Jul 07 09:51:25 2013 +0200 @@ -13,6 +13,7 @@ #define PyEncoder_Check(op) PyObject_TypeCheck(op, &PyEncoderType) #define PyEncoder_CheckExact(op) (Py_TYPE(op) == &PyEncoderType) +static PyTypeObject *PyDecimalType; static PyTypeObject PyScannerType; static PyTypeObject PyEncoderType; @@ -117,6 +118,8 @@ encoder_encode_string(PyEncoderObject *s, PyObject *obj); static PyObject * encoder_encode_float(PyEncoderObject *s, PyObject *obj); +static PyObject * +encoder_encode_decimal(PyEncoderObject *s, PyObject *obj); #define S_CHAR(c) (c >= ' ' && c <= '~' && c != '\\' && c != '"') #define IS_WHITESPACE(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r')) @@ -1326,6 +1329,14 @@ } static PyObject * +encoder_encode_decimal(PyEncoderObject *s, PyObject *obj) +{ + /* Return the JSON representation of a Decimal object */ + /* Use a better Decimal formatting here? */ + return PyObject_Str(obj); +} + +static PyObject * encoder_encode_string(PyEncoderObject *s, PyObject *obj) { /* Return the JSON representation of a string */ @@ -1391,6 +1402,12 @@ Py_LeaveRecursiveCall(); return rv; } + else if (PyObject_IsInstance(obj, (PyObject*)PyDecimalType)) { + PyObject *encoded = encoder_encode_decimal(s, obj); + if (encoded == NULL) + return -1; + return _steal_accumulate(acc, encoded); + } else { PyObject *ident = NULL; if (s->markers != Py_None) { @@ -1543,6 +1560,11 @@ if (kstr == NULL) goto bail; } + else if (PyObject_IsInstance(key, (PyObject*)PyDecimalType)) { + kstr = encoder_encode_decimal(s, key); + if (kstr == NULL) + goto bail; + } 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.*/ @@ -1821,6 +1843,7 @@ PyObject* PyInit__json(void) { + PyObject *decimal; PyObject *m = PyModule_Create(&jsonmodule); if (!m) return NULL; @@ -1840,6 +1863,14 @@ Py_DECREF((PyObject*)&PyEncoderType); goto fail; } + /* Get a reference to the Decimal type and store it in static PyDecimalType + declared above */ + decimal = PyImport_ImportModule("decimal"); + if (!decimal) + goto fail; + PyDecimalType = (PyTypeObject*)PyObject_GetAttrString(decimal, "Decimal"); + Py_DECREF(decimal); + Py_INCREF(PyDecimalType); return m; fail: Py_DECREF(m);