36

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>
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Dinesh
  • 4,437
  • 5
  • 40
  • 77
  • Both the second answer on the linked page and [the actual Python docs](http://docs.python.org/2/library/json.html#json.JSONEncoder) 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 Jun 06 '13 at 08:36
  • 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 Jun 06 '13 at 08:39

3 Answers3

81

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.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
22

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}'
FearlessFuture
  • 2,250
  • 4
  • 19
  • 25
  • 1
    Thanks for the intro to `simplejson` – Dinesh Sep 16 '15 at 20:45
  • 2
    It seems simplejson has it's days numbered though, at least as a default part of Django https://docs.djangoproject.com/en/1.8/internals/deprecation/#deprecation-removed-in-1-7 – wasabigeek Nov 29 '15 at 07:08
  • 6
    Are there any downsides using simplejson like this? Otherwise why so many answers go it the hard way? – benjaminz Mar 04 '16 at 05:36
22

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)
François Constant
  • 5,531
  • 1
  • 33
  • 39