For those who don't want to use a third-party library... An issue with Elias Zamaria's answer is that it converts to float, which can run into problems. For example:
>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 1e-07}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01733}'
The JSONEncoder.encode() method lets you return the literal json content, unlike JSONEncoder.default(), which has you return a json compatible type (like float) that then gets encoded in the normal way. The problem with encode() is that it (normally) only works at the top level. But it's still usable, with a little extra work (python 3.x):
import json
from collections.abc import Mapping, Iterable
from decimal import Decimal, Context, MAX_PREC
_context = Context(prec=MAX_PREC) # optional, to handle more than the default 28 digits, if necessary
class DecimalEncoder(json.JSONEncoder):
def encode(self, obj):
if isinstance(obj, Mapping):
return '{' + ', '.join(f'{self.encode(k)}: {self.encode(v)}' for (k, v) in obj.items()) + '}'
if isinstance(obj, Iterable) and (not isinstance(obj, str)):
return '[' + ', '.join(map(self.encode, obj)) + ']'
if isinstance(obj, Decimal):
# (the _context is optional, for handling more than 28 digits)
return f'{obj.normalize(_context):f}' # using normalize() gets rid of trailing 0s, using ':f' prevents scientific notation
return super().encode(obj)
Which gives you:
>>> json.dumps({'x': Decimal('0.0000001')}, cls=DecimalEncoder)
'{"x": 0.0000001}'
>>> json.dumps({'x': Decimal('100000000000.01734')}, cls=DecimalEncoder)
'{"x": 100000000000.01734}'
>>> json.dumps({'x': Decimal('123450.1000000000000000000000000000001000')}, cls=DecimalEncoder)
'{"x": 123450.1000000000000000000000000000001}'
As noted by Nickolay, if you're using more than 28 significant digits, you can run into issues with Decimal's default precision. So the above (modified) code includes a Context using MAX_PREC. There isn't really a performance impact, so it's fine to use a custom context like this even if you're not expecting that many digits.