34
votes

I have a python object which includes some decimals. This is causing the json.dumps() to break.

I got the following solution from SO (e.g. Python JSON serialize a Decimal object) but the recoomended solution still does not work. Python website - has the exact same answer.

Any suggestions how to make this work?

Thanks. Below is my code. It looks like the dumps() doesn't even go into the specialized encoder.

clayton@mserver:~/python> cat test1.py
import json, decimal

class DecimalEncoder(json.JSONEncoder):
        def _iterencode(self, o, markers=None):
                print "here we go o is a == ", type(o)
                if isinstance(o, decimal.Decimal):
                        print "woohoo! got a decimal"
                        return (str(o) for o in [o])
                return super(DecimalEncoder, self)._iterencode(o, markers)

z = json.dumps( {'x': decimal.Decimal('5.5')}, cls=DecimalEncoder )
print z
clayton@mserver:~/python> python test1.py
Traceback (most recent call last):
  File "test1.py", line 11, in <module>
    z = json.dumps( {'x': decimal.Decimal('5.5')}, cls=DecimalEncoder )
  File "/home/clayton/python/Python-2.7.3/lib/python2.7/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/home/clayton/python/Python-2.7.3/lib/python2.7/json/encoder.py", line 201, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/home/clayton/python/Python-2.7.3/lib/python2.7/json/encoder.py", line 264, in iterencode
    return _iterencode(o, 0)
  File "/home/clayton/python/Python-2.7.3/lib/python2.7/json/encoder.py", line 178, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: Decimal('5.5') is not JSON serializable
clayton@mserver:~/python>
3
Both the second answer on the linked page and the actual Python docs give the correct answer, which is to override default. It should be obvious that you shouldn't be overriding _iterencode, as the leading underscore implies that that is an internal-only method. - Daniel Roseman
json doesn't know how to handle Decimal so one should extend JSON encoder for Python data structureshttp://docs.python.org/2/library/json.html#py-to-json-table - oleg

3 Answers

78
votes

It is not (no longer) recommended you create a subclass; the json.dump() and json.dumps() functions take a default function:

def decimal_default(obj):
    if isinstance(obj, decimal.Decimal):
        return float(obj)
    raise TypeError

json.dumps({'x': decimal.Decimal('5.5')}, default=decimal_default)

Demo:

>>> def decimal_default(obj):
...     if isinstance(obj, decimal.Decimal):
...         return float(obj)
...     raise TypeError
... 
>>> json.dumps({'x': decimal.Decimal('5.5')}, default=decimal_default)
'{"x": 5.5}'

The code you found only worked on Python 2.6 and overrides a private method that is no longer called in later versions.

22
votes

I can't believe that no one here talked about using simplejson, which supports deserialization of Decimal out of the box.

import simplejson
from decimal import Decimal

simplejson.dumps({"salary": Decimal("5000000.00")})
'{"salary": 5000000.00}'

simplejson.dumps({"salary": Decimal("1.1")+Decimal("2.2")-Decimal("3.3")})
'{"salary": 0.0}'
18
votes

If you're using Django. There is a great class for Decimal and date fields:

https://docs.djangoproject.com/en/1.10/topics/serialization/#djangojsonencoder

To use it:

import json
from django.core.serializers.json import DjangoJSONEncoder

json.dumps(value, cls=DjangoJSONEncoder)