1. Basic JsonSerializableMixin

Quickly dumps all attributes into JSON

import json

class JsonSerializableMixin:
    def to_json(self):
        return json.dumps(vars(self))

class Product(JsonSerializableMixin):
    def __init__(self, name, price):
        self.name = name
        self.price = price

print(Product("Laptop", 1200).to_json())
# {"name": "Laptop", "price": 1200}

=> Fast and simple

BUT: Doesn’t handle nested objects or special types.

2. Custom Field Whitelist

Only include specific fields (__json_fields__)

import json

class JsonWhitelistMixin:
    __json_fields__ = []

    def to_json(self):
        data = {field: getattr(self, field) for field in self.__json_fields__ if hasattr(self, field)}
        return json.dumps(data)


class User(JsonWhitelistMixin):
    __json_fields__ = ["username", "email"]

    def __init__(self, username, email, password):
        self.username = username
        self.email = email
        self.password = password  # excluded


print(User("Joe", "joe@glinteco.com", "amazing Python team").to_json())
# {"username": "Joe", "email": "joe@glinteco.com"}

=> Great for security (hide passwords, tokens, etc.).

3. Nested Object Support

Handles lists, dicts, and other objects with to_json() or to_dict()

import json

class JsonNestedMixin:
    def to_dict(self):
        def convert(obj):
            if isinstance(obj, list):
                return [convert(i) for i in obj]
            if isinstance(obj, dict):
                return {k: convert(v) for k, v in obj.items()}
            if hasattr(obj, "to_dict"):
                return obj.to_dict()
            return obj

        return {k: convert(v) for k, v in vars(self).items()}

    def to_json(self):
        return json.dumps(self.to_dict())


class Address(JsonNestedMixin):
    def __init__(self, city, country):
        self.city = city
        self.country = country


class Person(JsonNestedMixin):
    def __init__(self, name, addresses):
        self.name = name
        self.addresses = addresses


p = Person("Glinteco", [Address("Hanoi", "Vietnam"), Address("Melbourne", "Australia")])
print(p.to_json())
# {"name": "Glinteco", "addresses": [{"city": "Hanoi", "country": "Vietnam"}, {"city": "Melbourne", "country": "Australia"}]}

=> Handles real-world object graphs.

4. Custom Encoder for Non-JSON Types

Supports datetime, Decimal, or other special types

import json, datetime, decimal


class CustomEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return obj.isoformat()
        if isinstance(obj, decimal.Decimal):
            return float(obj)
        return super().default(obj)


class JsonCustomEncoderMixin:
    def to_json(self):
        return json.dumps(vars(self), cls=CustomEncoder)


class Invoice(JsonCustomEncoderMixin):
    def __init__(self, amount, issued_at):
        self.amount = decimal.Decimal(amount)
        self.issued_at = issued_at


print(Invoice("1200.50", datetime.datetime(2025, 9, 6, 14, 30)).to_json())
# {"amount": 1200.5, "issued_at": "2025-09-06T14:30:00"}

=> Works with real-world data types that break vanilla json.dumps.