Skip to main content

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)

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: When json.dumps encounters the Decimal object price, it calls str(price), which returns "19.9900". This string is JSON serializable.
note

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 obj is a Decimal. If so, we return str(obj).
  • Otherwise, we call the parent class's default method (super().default(obj)) to handle standard types or raise the appropriate TypeError for other unhandled types.
  • You pass your custom class to json.dumps() using the cls argument: 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).

  1. Install simplejson:
    pip install simplejson
    # Or: python -m pip install simplejson
  2. 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.
note

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.Decimal is used for exact precision, which standard floating-point numbers often lack (e.g., 0.1 + 0.2 is not exactly 0.3 in binary floating-point). JSON's number type is typically implemented using floats. Serializing a Decimal as 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? (simplejson default): If the precision loss associated with standard floats is acceptable for your application, or if the receiving system expects JSON numbers directly, using simplejson (or potentially default=float in json.dumps, though less common for Decimal) 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:

  1. Use json.dumps(data, default=str): This converts Decimal objects to strings, preserving their exact precision, which is crucial for financial or scientific data. This is generally the best practice.
  2. Create a custom JSONEncoder subclass: Override the default method to convert Decimal to str. This is useful for reusable or more complex serialization logic.
  3. Use the simplejson library: It offers native Decimal support (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.