Python 3.x cannot serialize Decimal() to JSON
Solution 1
That answer turned out to be outdated and there was another answer with the working solution:
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, decimal.Decimal):
return str(o)
return super(DecimalEncoder, self).default(o)
Note that this will convert the decimal to its string representation (e.g.; "1.2300"
) to a. not lose significant digits and b. prevent rounding errors.
Solution 2
I wanted to serialize Decimal to JSON, and also deserialize it back to actual Decimal class objects in a dictionary elsewhere.
Here is my sample program which works for me (Python 3.6):
import json
from decimal import Decimal
import decimal
class DecimalEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, decimal.Decimal):
return {'__Decimal__': str(obj)}
# Let the base class default method raise the TypeError
return json.JSONEncoder.default(self, obj)
def as_Decimal(dct):
if '__Decimal__' in dct:
return decimal.Decimal(dct['__Decimal__'])
return dct
sample_dict = {
"sample1": Decimal("100"),
"sample2": [ Decimal("2.0"), Decimal("2.1") ],
"sample3": Decimal("3.1415"),
"other": "hello!"
}
print("1. sample_dict is:\n{0}\n".format(sample_dict))
sample_dict_encoded_as_json_string = json.dumps(sample_dict, cls=DecimalEncoder)
print("2. sample_dict_encoded_as_json_string is:\n{0}\n".format(sample_dict_encoded_as_json_string))
sample_dict_recreated = json.loads(sample_dict_encoded_as_json_string, object_hook=as_Decimal)
print("3. sample_dict_recreated is:\n{0}\n".format(sample_dict_recreated))
And here is the output:
1. sample_dict is:
{'sample1': Decimal('100'), 'sample2': [Decimal('2.0'), Decimal('2.1')], 'sample3': Decimal('3.1415'), 'other': 'hello!'}
2. sample_dict_encoded_as_json_string is:
{"sample1": {"__Decimal__": "100"}, "sample2": [{"__Decimal__": "2.0"}, {"__Decimal__": "2.1"}], "sample3": {"__Decimal__": "3.1415"}, "other": "hello!"}
3. sample_dict_recreated is:
{'sample1': Decimal('100'), 'sample2': [Decimal('2.0'), Decimal('2.1')], 'sample3': Decimal('3.1415'), 'other': 'hello!'}
Hope this helps!
LLL
Software developer Interested in .net core, aws, distributed cloud-native systems Certified AWS Developer Associate
Updated on January 30, 2021Comments
-
LLL over 3 years
I asked this question earlier, and it was marked as duplicate of this, but the accepted answer does not work and even pylint shows that there are errors in the code.
What I want to do:
from decimal import Decimal import json thang = { 'Items': [{'contact_id': Decimal('2'), 'street_name': 'Asdasd'}, {'contact_id': Decimal('1'), 'name': 'Lukas', 'customer_id': Decimal('1')}], 'Count': 2} print(json.dumps(thang))
This throws:
TypeError: Object of type 'Decimal' is not JSON serializable
So I tried the linked answer:
from decimal import Decimal import json thang = { 'Items': [{'contact_id': Decimal('2'), 'street_name': 'Asdasd'}, {'contact_id': Decimal('1'), 'name': 'Lukas', 'customer_id': Decimal('1')}], 'Count': 2} class DecimalEncoder(json.JSONEncoder): def _iterencode(self, o, markers=None): if isinstance(o, Decimal): # wanted a simple yield str(o) in the next line, # but that would mean a yield on the line with super(...), # which wouldn't work (see my comment below), so... return (str(o) for o in [o]) return super(DecimalEncoder, self)._iterencode(o, markers) print(json.dumps(thang, cls=DecimalEncoder))
And here the linter shows that line
return super(DecimalEncoder, self)._iterencode(o, markers)
has errors, becauseSuper of 'DecimalEncoder' has no '_iterencode' member
and when ran throwsTypeError: Object of type 'Decimal' is not JSON serializable
How do I make this work?
-
Cochise Ruhulessin about 6 yearsThis quite defeats the purpose of the
decimal.Decimal
class. It should serialize to a string instead of a float, since float is not accurate. -
Zephaniah Grunschlag almost 4 yearsTotally agree. An answer that serializes a Decimal to a float should not be the top answer as it is dangerously inaccurate. For example, you can end up violating a legal contract because of a floating point rounding error.