Skip to main content

How to Convert Python Dataclasses to JSON

Python's dataclasses provide a convenient way to create classes that primarily store data.

This guide explains how to serialize dataclass instances to JSON strings, a common requirement for data exchange and storage. We'll cover the standard dataclasses.asdict() method, creating custom JSON encoders for more complex scenarios, and using the dataclasses-json library.

Converting Dataclasses to JSON with asdict() and json.dumps()

The most straightforward and recommended way to convert a dataclass to JSON is to:

  1. Convert the dataclass to a dictionary using dataclasses.asdict().
  2. Serialize the dictionary to JSON using json.dumps().
import json
from dataclasses import dataclass, asdict

@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_available: int = 0

item1 = InventoryItem('A', 100, 1)
print(item1) # Output: InventoryItem(name='A', unit_price=100, quantity_available=1)
json_str = json.dumps(asdict(item1)) # Convert to dict, then to JSON

print(json_str)
# Output: {"name": "A", "unit_price": 100, "quantity_available": 1}

print(type(json_str))
# Output: <class 'str'>
  • @dataclass: This decorator automatically generates methods like __init__, __repr__, etc., for your class, making it concise.
  • asdict(item1): This function from the dataclasses module recursively converts the dataclass instance (item1) into a standard Python dictionary. This is the crucial step, as json.dumps() can handle dictionaries directly.
  • json.dumps(...): This function from the json module serializes the dictionary into a JSON-formatted string.

Custom JSON Encoding with JSONEncoder

For more control over the JSON serialization process (e.g., handling custom data types or formatting), you can create a custom JSONEncoder:

import json
from dataclasses import dataclass, asdict, is_dataclass

@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_available: int = 0

class DCJSONEncoder(json.JSONEncoder):
def default(self, o):
if is_dataclass(o): # Check if object is dataclass instance.
return asdict(o) # Use asdict to serialize
return super().default(o) # Use default method for other types.

item1 = InventoryItem('A', 100, 1)
json_str = json.dumps(item1, cls=DCJSONEncoder)
print(json_str) # Output: {"name": "A", "unit_price": 100, "quantity_available": 1}
  • class DCJSONEncoder(json.JSONEncoder):: We create a custom encoder by subclassing json.JSONEncoder.
  • def default(self, o):: This method is called by json.dumps() when it encounters an object it doesn't know how to serialize.
    • if is_dataclass(o):: We check if the object o is a dataclass instance.
    • return asdict(o): If it's a dataclass, we use asdict() to convert it to a dictionary, which json.dumps() can handle.
    • return super().default(o): If it's not a dataclass, we call the default() method of the parent class (JSONEncoder). This handles the standard types (lists, dictionaries, strings, numbers, etc.) and raises a TypeError if it encounters an unsupported type. This is essential for correct behavior.
  • json.dumps(item1, cls=DCJSONEncoder): We tell json.dumps() to use our custom encoder.

Using the dataclasses-json Library

The dataclasses-json library provides a convenient way to convert dataclasses to and from JSON:

  • First you need to install the package using pip install dataclasses-json.
from dataclasses import dataclass
from dataclasses_json import dataclass_json

@dataclass_json
@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_available: int = 0

item1 = InventoryItem('A', 100, 1)
print(item1) # Output: InventoryItem(name='A', unit_price=100, quantity_available=1)

json_str = item1.to_json()
print(json_str) # Output: {"name": "A", "unit_price": 100, "quantity_available": 1}
dataclass_obj = InventoryItem.from_json(json_str)
print(dataclass_obj) # Output: InventoryItem(name='A', unit_price=100.0, quantity_available=1)
  • @dataclass_json: This decorator adds methods like to_json() and from_json() to your dataclass.
  • You don't have to use the dict class before calling json.dumps().
  • item1.to_json(): Converts the dataclass instance to a JSON string.
  • InventoryItem.from_json(): Creates a class instance from the JSON string.

While json.dumps() accepts a default argument, it is less flexible and less maintainable than creating a custom JSONEncoder:

import json
from dataclasses import dataclass, asdict

@dataclass
class InventoryItem:
name: str
unit_price: float
quantity_available: int = 0

item1 = InventoryItem('A', 100, 1)

# Avoid using lambda with json.dumps()
json_str = json.dumps(item1, default=lambda o: asdict(o) if isinstance(o, InventoryItem) else o)

print(json_str) # Output: {"name": "A", "unit_price": 100.0, "quantity_available": 1}
note

It's less readable because the custom logic is defined using a lambda expression.