How to Resolve Python "TypeError: Object of type Decimal is not JSON serializable"
When working with precise numerical data in Python using the decimal module, you might encounter the TypeError: Object of type Decimal is not JSON serializable error when trying to serialize data containing Decimal objects into a JSON string using the standard json library. This happens because the default JSON encoder doesn't natively recognize or know how to convert Decimal objects.
This guide explains why this error occurs and details effective methods to serialize Decimal objects correctly.
Understanding the Error: JSON Serializable Types
JSON (JavaScript Object Notation) has a limited set of standard data types: objects (maps/dictionaries), arrays (lists/tuples), strings, numbers (integers/floats), booleans (true/false), and null. Python's built-in json module knows how to convert standard Python types (dict, list, tuple, str, int, float, bool, None) into their corresponding JSON representations.
However, specialized types like decimal.Decimal, datetime.datetime, or custom class instances are not directly supported by the default encoder.
The Cause: json.dumps() and Decimal Objects
The TypeError occurs because the json.dumps() function (which serializes a Python object into a JSON formatted string) encounters a Decimal object and doesn't have a built-in rule for converting it to a standard JSON type.
import json
from decimal import Decimal
# Create a Decimal object (preserves exact precision)
price = Decimal('19.99')
data = {'item': 'Book', 'price': price}
print(f"Data contains type: {type(data['price'])}") # Output: <class 'decimal.Decimal'>
try:
# ⛔️ TypeError: Object of type Decimal is not JSON serializable
# Default json.dumps doesn't know how to handle Decimal
json_output = json.dumps(data)
print(json_output)
except TypeError as e:
print(e)
Solution 1: Using the default Argument with str (Recommended for Precision)
The json.dumps() function accepts a default keyword argument. You can provide a function to default that will be called for any object the encoder doesn't recognize. A simple and effective solution for Decimal is to convert it to a string, which preserves its exact precision.
import json
from decimal import Decimal
price = Decimal('19.9900') # Note the trailing zeros
data = {'item': 'Book', 'price': price}
# ✅ Provide the built-in str function to the 'default' argument
json_output = json.dumps(data, default=str)
print(f"Serialized JSON (Decimal as string): {json_output}")
# Output: Serialized JSON (Decimal as string): {"item": "Book", "price": "19.9900"}
print(f"Output type: {type(json_output)}") # Output: <class 'str'>
default=str: Whenjson.dumpsencounters theDecimalobjectprice, it callsstr(price), which returns"19.9900". This string is JSON serializable.
This method guarantees that the exact decimal value, including trailing zeros indicating precision, is preserved in the JSON output as a string. The receiving system can then parse this string back into a high-precision decimal type if needed.
Solution 2: Creating a Custom JSONEncoder Subclass
For more complex scenarios or reusable logic, you can create a custom encoder by subclassing json.JSONEncoder and overriding its default() method.
import json
from decimal import Decimal
class DecimalEncoder(json.JSONEncoder):
def default(self, obj):
# Check if the object is an instance of Decimal
if isinstance(obj, Decimal):
# Convert Decimal to string to preserve precision
return str(obj)
# Let the base class default method handle other types
# or raise a TypeError for unsupported types.
return super(DecimalEncoder, self).default(obj)
# Example usage
price = Decimal('0.1') # A value often imprecise as float
inventory = [Decimal('1.5'), Decimal('2.0')]
data = {'cost': price, 'stock': inventory}
# ✅ Use the custom encoder via the 'cls' argument
json_output = json.dumps(data, cls=DecimalEncoder)
print(f"Serialized JSON (Custom Encoder): {json_output}")
# Output: Serialized JSON (Custom Encoder): {"cost": "0.1", "stock": ["1.5", "2.0"]}
- The
default(self, obj)method receives objects the standard encoder can't handle. - We check if
objis aDecimal. If so, we returnstr(obj). - Otherwise, we call the parent class's
defaultmethod (super().default(obj)) to handle standard types or raise the appropriateTypeErrorfor other unhandled types. - You pass your custom class to
json.dumps()using theclsargument:cls=DecimalEncoder.
Solution 3: Using the simplejson Library
The third-party simplejson library is an alternative JSON encoder/decoder that offers more features, including native support for Decimal objects (often enabled by default).
- Install
simplejson:pip install simplejson
# Or: python -m pip install simplejson - Use
simplejson.dumps():from decimal import Decimal
# Import simplejson, often aliased as json for compatibility drop-in
import simplejson as json
price = Decimal('19.99')
data = {'item': 'Book', 'price': price}
# simplejson handles Decimal natively (usually as a number/float)
# The use_decimal=True argument ensures Decimal serialization (often the default)
json_output = json.dumps(data, use_decimal=True)
print(f"Serialized JSON (simplejson): {json_output}")
# Output: Serialized JSON (simplejson): {"item": "Book", "price": 19.99}
print(f"Output type: {type(json_output)}") # Output: <class 'str'>
# Note: The 'price' value in the JSON is now a number, not a string.
simplejson often serializes Decimal directly as a JSON number (which might be interpreted as a float by the receiver). This is convenient but might lead to precision loss if the receiver doesn't handle it carefully (see next section).
Important Consideration: String vs. Float for Precision
- Why
str? (Solutions 1 & 2):decimal.Decimalis used for exact precision, which standard floating-point numbers often lack (e.g.,0.1 + 0.2is not exactly0.3in binary floating-point). JSON's number type is typically implemented using floats. Serializing aDecimalas a string ("19.9900") guarantees that the exact value is transmitted without potential floating-point rounding errors. The receiving system must then be aware that this string represents a decimal and parse it accordingly. This is the standard way to exchange high-precision decimal values via JSON. - Why
float? (simplejsondefault): If the precision loss associated with standard floats is acceptable for your application, or if the receiving system expects JSON numbers directly, usingsimplejson(or potentiallydefault=floatinjson.dumps, though less common forDecimal) might be simpler. However, you lose the guarantee of exact precision.
Conclusion
The TypeError: Object of type Decimal is not JSON serializable occurs because Python's default json encoder doesn't natively support the decimal.Decimal type.
The recommended solutions are:
- Use
json.dumps(data, default=str): This convertsDecimalobjects to strings, preserving their exact precision, which is crucial for financial or scientific data. This is generally the best practice. - Create a custom
JSONEncodersubclass: Override thedefaultmethod to convertDecimaltostr. This is useful for reusable or more complex serialization logic. - Use the
simplejsonlibrary: It offers nativeDecimalsupport (usually serializing as a JSON number/float), which might be convenient if exact precision is less critical or if the receiver expects numbers.
Choose the method based on whether preserving the exact precision of the Decimal (by converting to a string) is required for your application.