Fundamentals 6 min read

Using functools.singledispatch for Extensible JSON Serialization in Python

The article explains how Python's functools.singledispatch (PEP 443) provides a clean, extensible way to serialize arbitrary objects to JSON, avoiding custom default functions or subclassing JSONEncoder, and discusses alternatives, pitfalls, and related tools such as zope.interface and attrs.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Using functools.singledispatch for Extensible JSON Serialization in Python

Many Python standard libraries contain under‑appreciated gems, and one of them is the ability to perform simple, elegant function dispatch based on argument types. This feature is ideal for serializing arbitrary objects, such as when producing JSON for web APIs or structured logs.

Typical approaches involve either writing a default() function that knows how to convert every custom type, or subclassing json.JSONEncoder and passing it via the cls argument to json.dump() . Both methods require the serializer to be aware of every type up front, which limits extensibility.

Because third‑party libraries often mimic the json module API, they inherit the same lack of extensibility. A more scalable solution is to register adapters in a central registry, similar to Pyramid's JSON.add_adapter that leverages the zope.interface adapter registry.

Django provides its own DjangoJSONEncoder , a subclass of json.JSONEncoder , which knows how to encode dates, times, UUIDs, etc., but beyond that you still need a custom solution. For deeper integration, Django REST framework offers a full serialization system.

One alternative some developers use is adding a __structlog__ method to classes, akin to __str__ , but this couples serialization logic to the class definition and is generally discouraged.

Python 3.4 introduced a better answer to this problem via PEP 443: functools.singledispatch . It allows you to define a generic function and register type‑specific implementations, enabling clean, decentralized serialization without large if‑elif‑else chains.

With singledispatch , you can register a handler for datetime objects that calls a to_serializable() method, and the dispatcher will automatically select the correct function based on the argument type.

This approach lets you place your serializers wherever you like—inside the class, in a separate module, or alongside your JSON handling code—while keeping the class itself free of serialization concerns.

Further notes:

Some fast JSON libraries like UltraJSON and python‑rapidjson only support a default() hook and cannot use singledispatch ‑style extensibility.

The attrs library can help manage data classes that work well with singledispatch .

Pyramid’s adapter‑based API, originally from zope.component , lacks comprehensive documentation.

The original motivation for adding single‑dispatch to the standard library was to provide a cleaner implementation of pprint , though that never materialized.

In summary, functools.singledispatch offers a powerful, centralized yet decoupled way to register serialization logic for any Python object without modifying the objects themselves.

PythonSerializationJSONextensibilityfunctoolssingledispatch
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.