PEP: 837
Title: Extensible JSON serialization
Author: Serhiy Storchaka <storchaka@gmail.com>
Discussions-To: https://discuss.python.org/t/pep-837-extensible-json-serialization/108124
Status: Draft
Type: Standards Track
Created: 12-Jul-2026
Python-Version: 3.16
Post-History: `12-Jul-2026 <https://discuss.python.org/t/pep-837-extensible-json-serialization/108124>`__


Abstract
========

This PEP adds an extension mechanism to the :mod:`json` encoder
consisting of three complementary parts, one per level of
customization:

* **library level**: a serialization protocol — the special methods
  ``__json__()`` and ``__raw_json__()``;

* **application level**: a global registry —
  ``copyreg.json(cls, function)`` filling
  ``copyreg.json_dispatch_table``, following the precedent of
  :func:`copyreg.pickle`;

* **call level**: a per-encoder dispatch table — a ``dispatch_table``
  attribute on a :class:`json.JSONEncoder`, mirroring
  :attr:`pickle.Pickler.dispatch_table`.

The more specific level takes precedence.

A helper class ``copyreg.RawJSON`` wraps an already-encoded JSON string
so that it is emitted verbatim, enabling representations that are not
otherwise expressible (such as serializing :class:`decimal.Decimal` as a
JSON number with full precision).

Several standard library container types whose JSON form is unambiguous —
:class:`collections.deque`, :class:`types.MappingProxyType`,
:class:`collections.ChainMap`, :class:`collections.UserDict`,
:class:`collections.UserList` and :class:`collections.UserString` —
gain a ``__json__`` method and therefore serialize out of the box.

The :mod:`json` module itself gains no new public names.


Motivation
==========

Serializing anything beyond the basic types (``dict``, ``frozendict``,
``list``, ``tuple``, ``str``, ``int``, ``float``, ``bool``, ``None``)
with :func:`json.dumps` today requires either passing a ``default=``
function to every call, or subclassing :class:`json.JSONEncoder` and
threading the subclass through every call site.  Both mechanisms attach
the knowledge to the *call* rather than to the *type*:

* **They do not compose.**  Two libraries that each need a custom
  ``default=`` cannot both have it applied to one document without the
  application writing a merging wrapper by hand.  A library that
  serializes internally (for logging, caching, IPC) cannot see the
  application's ``default=`` at all.

* **Third-party types cannot opt in.**  A library defining a new type
  has no way to make it JSON-serializable for its users; every user must
  learn and repeat the incantation.  Requests to fix this for NumPy
  scalars and arrays (:cpython-issue:`68501`, :cpython-issue:`62503`) and
  for D-Bus integer types (:cpython-issue:`77978`) were closed as
  out of scope for the standard library — correctly, but leaving the
  underlying need unmet.

* **Some representations are impossible.**  ``default=`` must return one
  of the basic types, so there is no way to emit a non-integer JSON
  *number* that ``float`` cannot represent exactly.  :func:`json.loads`
  can read decimal numbers losslessly via
  ``parse_float=decimal.Decimal``, but the result cannot be written
  back (:cpython-issue:`67312`).  The same limitation blocks control over float
  formatting (:cpython-issue:`81022`) and non-finite float representation
  (:cpython-issue:`98306`, :cpython-issue:`134717`), and the general request
  for emitting pre-encoded JSON (:cpython-issue:`86957`).

The demand is long-standing and broad.  Beyond the general proposals for
a serialization hook checked before raising :exc:`TypeError`
(:cpython-issue:`71549` — open since 2016, :cpython-issue:`79292`,
:cpython-issue:`114285`, :cpython-issue:`86931`), the tracker accumulated
requests for out-of-the-box support of concrete standard library types:
:class:`decimal.Decimal` (:cpython-issue:`67312`, :cpython-issue:`118810`,
:cpython-issue:`145115`), :class:`array.array` (:cpython-issue:`70451`),
:class:`collections.deque` (:cpython-issue:`64973`, :cpython-issue:`73849`),
:class:`types.MappingProxyType` (:cpython-issue:`79039`),
:func:`collections.namedtuple` as an object (:cpython-issue:`67661`), and
:class:`datetime.datetime` (:cpython-issue:`65742`); and for whole
categories: sets, frozensets, bytearrays and iterators
(:cpython-issue:`75338`), generators (:cpython-issue:`78031`), and
dictionary views (:cpython-issue:`83377`).

The idea has also been proposed independently for sixteen years, each
time reinventing part of this design: a ``__json__()`` method on
python-ideas in `July 2010`_
and `April 2020`_
and on `discuss.python.org in 2022–2024`_;
raw output and a standardized encoder protocol on `python-ideas in 2019`_;
a registration function on `discuss.python.org in 2022`_.

Meanwhile the ecosystem adopted the convention piecemeal.  `TurboGears`_
serializes objects via a no-argument ``__json__()`` — and its
``custom_encoders`` configuration takes precedence over it, the same
ordering as this PEP.  `Pyramid's JSON renderer`_
calls ``__json__(request)``.  Applications and libraries such as conda,
Checkmk and TatSu define ``__json__`` today.  `simplejson`_ added an opt-in ``for_json()``
method instead — choosing that name precisely because dunder names are
reserved for the language (`simplejson issue #52`_).  In that issue's
discussion, a GitHub code search already found more ``__json__``
definitions in the wild than ``for_json`` ones by 2016, and formalizing
the protocol in a PEP was requested so that third-party libraries can
rely on the same signature.  Only the standard library can define
``__json__``; this PEP does, ending the fragmentation.

The type-support requests could not simply be granted one by one.  For
most of the requested types the JSON representation is *ambiguous*:

* ``Decimal`` — a JSON number (``1234567890.0987654321``) or a JSON
  string (``"1234567890.0987654321"``)?  Both are common; each is wrong
  for some consumers.
* ``namedtuple`` — a JSON Array (it is a tuple) or a JSON Object (it has
  named fields)?
* ``array.array`` — a JSON Array of numbers, or a compact encoded
  string?  The sensible answer even depends on its type code.
* ``datetime`` — which of the many string representations (ISO 8601
  variants, epoch seconds, RFC formats)?

No standard library default can be correct, so the standard library
should provide the *mechanism* by which each application declares its
*policy*.  That mechanism is this PEP.


Rationale
=========

One system covering all of the collected needs
----------------------------------------------

The feature is deliberately a small *system* rather than a single hook:
each part answers a different cluster of the requests above, and no
part alone covers them all.

* The ``__json__`` **protocol** answers the general extension proposals
  and the third-party-type reports: the type's author makes it
  serializable once, with no imports, inherited by subclasses.

* The **registry** answers the requests for types the requester does
  not control or whose representation is a policy choice (``Decimal``,
  ``namedtuple``, ``array.array``, ``datetime``): the application
  declares its policy in one line.

* **Raw output** (``__raw_json__``/``RawJSON``) answers the requests
  for representations that no basic-type conversion can express:
  pre-encoded fragments, full-precision numbers, float formatting,
  non-finite floats.

* **Duck typing of hook results** answers the category requests
  (iterables, sets, generators, dictionary views, mappings):
  ``copyreg.json(set, sorted)`` is a complete solution for sets, and a
  hook may return any iterable or mapping-like object rather than
  materializing a list or dict.

* **Standard library** ``__json__`` **methods** answer the requests for
  unambiguous containers (``deque``, ``mappingproxy``) directly.

* The **per-encoder** ``dispatch_table`` scopes any of the above to one
  encoder when a process needs different policies for different
  outputs.

Three levels of customization
-----------------------------

The three parts correspond to the three levels at which serialization
is decided, and deliberately have different shapes:

* **Library level** — ``__json__()`` is for the author of a type.  A
  method needs no imports and is inherited by subclasses: a library
  can make its types JSON-serializable without depending on
  :mod:`json` or :mod:`copyreg` at all.

* **Application level** — ``copyreg.json()`` is for users of a type
  they do not control: the application chooses, process-wide, how
  third-party or ambiguous standard library types serialize.
  Registration is by exact type; subclasses that want the same
  treatment inherit ``__json__`` instead.

* **Call level** — the ``dispatch_table`` attribute on a
  :class:`json.JSONEncoder` subclass or instance customizes a
  particular call, when one program needs different policies for
  different outputs (an external API versus an internal cache, say).
  The existing ``default=`` parameter remains the call-level catch-all
  for objects no other mechanism handled.

The more specific level takes precedence: a registry entry is
consulted before the type's ``__json__`` (the application overrides
the library), and a per-encoder table replaces the global registry
(the call overrides the application).

Why the registry lives in copyreg
---------------------------------

The registry could have been ``json.register()``.  It is placed in
:mod:`copyreg` instead, for three reasons.

**Import cost and layering.**  A library that registers a serialization
function for a foreign type at import time should not pay for the
:mod:`json` module, whose import is dominated by :mod:`re` (roughly
fifty times the cost of importing :mod:`copyreg`, which is imported by
:mod:`copy` and :mod:`pickle` anyway).  With the registry and
``RawJSON`` both in :mod:`copyreg`, a registrant imports exactly one
small module.  The :mod:`json` module imports :mod:`copyreg` (as
:mod:`copy` already does), never the reverse.

**Precedent and symmetry.**  This is the pickle model, mirrored in both
halves: a registration function filling a global table
(:func:`copyreg.pickle` / ``copyreg.json()``) and a per-instance
``dispatch_table`` attribute overriding it (:class:`pickle.Pickler` /
:class:`json.JSONEncoder`).  The function name follows the
``copyreg.pickle()`` convention; thirty years of that precedent show
that a registration function named after its protocol causes no
confusion, because registration is a one-off, module-prefixed call.

**Neutral ground.**  Third-party JSON encoders can honor the same
registry and helper class without importing the standard :mod:`json`
module, so the ecosystem can converge on a single registration point.

Placing the registry in :mod:`copyreg` also generalizes the module's
charter — registering protocol implementations for types you do not
control.  Registries for other protocols (such as ``__copy__``- and
``__deepcopy__``-like hooks for the :mod:`copy` module) fit the same
pattern and will be proposed separately.

The raw-output mechanism
------------------------

Emitting an already-encoded fragment verbatim is the only way to express
representations outside the basic types, most importantly full-precision
numbers.  The chosen mechanism is a duck-typed special method,
``__raw_json__()``, with ``copyreg.RawJSON`` as a convenience wrapper:

* A hook (or ``__json__``) that wants raw output returns
  ``copyreg.RawJSON(fragment)`` — one import, which the registrant has
  already paid — or an instance of its own wrapper class defining just
  ``__raw_json__``, with no imports at all.

* ``RawJSON`` additionally defines ``__json__`` returning ``self`` to
  serve its second function: including a pre-serialized fragment
  *directly* in a document (as a value in a dict or list passed to
  :func:`json.dumps`), not only as a hook result.

The protocol, not the class, is the interface; the class is sugar.

Performance: the encoding pipeline as an invariant
--------------------------------------------------

The :mod:`json` encoder is among the hottest code in the standard
library, and the design treats its branch order as an invariant:

1. Objects of the exact basic types are encoded with **no attribute
   lookups at all** — the overwhelmingly common case pays nothing for
   this PEP.

2. The dispatch table and ``__json__`` are consulted next — one dict
   lookup plus one type-attribute lookup — *before* the ``isinstance``
   fallbacks, solely so that subclasses of the basic types can
   customize their serialization at all (an ``int`` subclass would
   otherwise be consumed by the ``isinstance(o, int)`` check, the very
   problem history records for ``IntEnum``: :cpython-issue:`62464`).

3. The ``isinstance`` fallbacks then catch non-customizing subclasses,
   preserving today's behavior for them exactly (``IntEnum`` still
   serializes as a number).

4. Duck typing of a hook result — ``__index__`` as a number,
   ``__float__`` as a number, ``__iter__`` with ``keys()`` as an
   object, ``__iter__`` alone as an array, ``__raw_json__`` verbatim —
   applies **only when a hook has fired**.  In particular,
   ``__raw_json__`` is never looked up on an object that did not come
   from a hook, so objects bound for the subclass checks or
   ``default()`` pay no extra lookups.

5. The ``default`` hook runs last, unchanged.

Which stdlib types get a default __json__
-----------------------------------------

The criterion: **a standard library type gets a default ``__json__``
only when its JSON form is unambiguous.**  Transparent containers
qualify — ``deque``, ``mappingproxy``, ``ChainMap``, ``UserDict``,
``UserList``, ``UserString`` are semantically just arrays and objects
(resolving :cpython-issue:`64973`, :cpython-issue:`73849` and
:cpython-issue:`79039` directly).  ``Decimal``, ``namedtuple``,
``array.array`` and ``datetime`` do not qualify, for the ambiguity
reasons given in the Motivation; for them, this PEP's answer is a
one-line registration by the application (see `How to Teach This`_).

This criterion is also the ready-made answer to future "just add
``__json__`` to X" requests.


Specification
=============

The __json__ protocol
---------------------

A class may define a method ``__json__(self)``.  When the :mod:`json`
encoder encounters an object that is not of an exactly-supported basic
type and has no dispatch-table entry, it looks up ``__json__`` on the
object's type (like all special methods) and, if present, calls it.
The result is then serialized in place of the object:

* A result of a basic type, or an instance of their subclasses, is
  serialized as usual.

* Otherwise the result is interpreted by duck typing: an object with
  ``__index__`` is serialized as a JSON number via
  :func:`operator.index`; else one with ``__float__`` as a JSON number
  via :class:`float`; else an iterable with a ``keys()`` method (and
  ``__getitem__``) as a JSON object; else an iterable as a JSON array;
  else an object whose type defines ``__raw_json__`` is serialized by
  calling it (see below).  Otherwise :exc:`TypeError` is raised.

The result of ``__json__`` is not recursively re-submitted to the
protocol: a result that is itself of a hook-defining type is not
converted again.

The __raw_json__ protocol
-------------------------

``__raw_json__(self)`` must return a :class:`str`, which the encoder
emits verbatim, without validation of its content.  It is consulted
only for objects produced by a dispatch-table function or ``__json__``
(see the pipeline invariant above), so a wrapper class used as a hook
result needs only ``__raw_json__``.  ``RawJSON`` also defines
``__json__`` returning ``self``, which lets its instances appear
directly in the input document.

The registry
------------

The following are added to :mod:`copyreg`:

``copyreg.json(ob_type, json_function)``
   The official registration interface.  Registers ``json_function``
   as the serialization function for ``ob_type``.  Raises
   :exc:`TypeError` if ``json_function`` is not callable.  The
   function is called with the object as its only argument and its
   result is interpreted exactly like the result of ``__json__``.
   Registration is by exact type: it does not apply to subclasses of
   ``ob_type``.

``copyreg.json_dispatch_table``
   The dict mapping types to registered functions, consulted by the
   :mod:`json` module (and available to other JSON encoders honoring
   the registry).  Applications register via ``copyreg.json()`` rather
   than mutating the table directly.

``copyreg.RawJSON(encoded_json)``
   A wrapper whose instances are serialized by emitting
   ``encoded_json`` verbatim.  ``str()`` of the instance returns the
   fragment.

Registered functions take precedence over ``__json__``.  Entries for
the exactly-supported basic types themselves (``dict``, ``list``,
``str``, ``int``, ...) are never consulted, because the fast paths for
those types run first.

The encoder
-----------

:class:`json.JSONEncoder` (and the C accelerator) consult
``getattr(encoder, 'dispatch_table', copyreg.json_dispatch_table)``, so
a subclass may set a ``dispatch_table`` class or instance attribute to
replace the global registry for that encoder, mirroring
:class:`pickle.Pickler`.

Dict *keys* are not affected by any part of this PEP: key serialization
accepts exactly the types it accepts today, and neither the dispatch
table nor ``__json__`` is consulted for keys (see Open Issues).

The ``check_circular`` machinery is unchanged: containers and
``default`` results are tracked as today.  Hook results are not
separately tracked; a pathological hook that returns a fresh cycle on
every call ends in :exc:`RecursionError` like any runaway recursion.

Standard library __json__ methods
---------------------------------

``__json__`` returning a view of the obvious container is added to:
:class:`collections.deque` and :class:`types.MappingProxyType` (in C,
returning ``self``; the encoder consumes them via the array and object
paths), and :class:`collections.ChainMap`,
:class:`collections.UserDict`, :class:`collections.UserList`,
:class:`collections.UserString` (in Python, returning ``self`` or the
underlying ``data``).

Backwards Compatibility
=======================

The :mod:`json` module gains no new public names and no existing
behavior changes for objects that define none of the new hooks:

* All exactly-supported basic types serialize byte-for-byte as before.

* Subclasses of basic types without hooks serialize as before
  (``IntEnum`` as a number, str subclasses as strings, dict and list
  subclasses as objects and arrays).

* Objects previously rejected with :exc:`TypeError` that define
  ``__json__`` will now serialize.  Classes following the TurboGears
  convention (a no-argument ``__json__``) get exactly the behavior
  they intended.  Classes written for Pyramid's renderer, whose
  ``__json__(self, request)`` takes an argument, keep raising
  :exc:`TypeError` — now from the missing argument rather than from
  ``default`` — so code catching the exception keeps working, though
  the message changes.  A class using the name for something unrelated
  would change behavior, but dunder names are reserved by the language
  reference, and no plausible unrelated meaning is known.

* The signature of the private ``_json.make_encoder`` gains a
  ``dispatch_table`` argument.

* The six standard library classes gaining ``__json__`` previously
  raised :exc:`TypeError` under :func:`json.dumps` (unless handled by
  ``default=``).  Code using ``default=`` to serialize them keeps
  working, because those objects no longer reach ``default`` — but the
  built-in representation (array/object) may differ from what a custom
  ``default`` produced.  This is the usual risk of any new default
  behavior and is judged acceptable for unambiguous containers.

* ``from copyreg import json`` shadows the :mod:`json` module name in
  the importing namespace, as ``from copyreg import pickle`` always
  has.  Documentation will show module-prefixed calls.


Security Implications
=====================

``__raw_json__`` and ``RawJSON`` emit strings without validation, so a
hook returning attacker-controlled fragments could produce invalid or
misleading JSON.  This is no new capability: ``default=`` already
executes arbitrary code during encoding, and producing malformed output
requires the application to have installed the hook.  The documentation
will note that raw fragments must come from trusted producers.


How to Teach This
=================

Four recipes, one per customization level:

1. *Library level* — your own class: define ``__json__``::

      class Money:
          def __json__(self):
              return {"amount": str(self.amount),
                      "currency": self.currency}

2. *Application level* — someone else's class: register it::

      import copyreg, decimal
      copyreg.json(decimal.Decimal, str)

3. *Application level* — a representation JSON cannot otherwise
   express: return a raw fragment::

      copyreg.json(decimal.Decimal,
                   lambda d: copyreg.RawJSON(str(d)))

4. *Call level* — one encoder, different policy::

      class APIEncoder(json.JSONEncoder):
          dispatch_table = {decimal.Decimal: str}

The existing ``default=`` remains the call-level catch-all for objects
no other mechanism handled and is documented as running last.


Reference Implementation
========================

:cpython-pr:`153607` (branch ``json-customize4``), a rebase and rework of the author's
2017–2022 ``json-customize`` branches: the protocol and registry with C
and Python encoder support, standard library ``__json__`` methods, and
tests for both encoder implementations, including the encoding-pipeline
invariant recorded as code comments.


Rejected Ideas
==============

``json.register()`` instead of copyreg
   Registration in the :mod:`json` module forces registrants to import
   it (~50× the import cost of :mod:`copyreg`, dominated by :mod:`re`),
   breaks the pickle symmetry, and gives third-party encoders no
   neutral registry.  Discoverability is addressed by documentation in
   the :mod:`json` docs pointing to :mod:`copyreg`.

``copyreg.register_json()``-style names
   Inconsistent with :func:`copyreg.pickle`, the thirty-year precedent.

Default ``__json__`` for Decimal, namedtuple, array.array, datetime
   Each has at least two legitimate JSON representations (number vs
   string; array vs object; array vs encoded string, depending on type
   code; many string formats).  A default would be wrong for a large
   fraction of consumers; the ambiguity is the argument for a registry,
   not for a default.

Serializing all iterables and mappings without opt-in
   Requested in :cpython-issue:`75338` and implied by
   :cpython-issue:`78031` and :cpython-issue:`83377`, but a silent behavior
   change: every object with ``__iter__`` (sets, generators, file
   objects) would begin serializing as an array instead of reaching
   ``default=`` or raising.  Duck typing therefore applies only to hook
   results — an explicit opt-in.

``isinstance(o, RawJSON)`` instead of ``__raw_json__``
   Rejected because it taxes the wrong audience: a type that opts in
   via ``__json__`` with zero imports would need to import
   :mod:`copyreg` the moment it needs raw output.  With the duck-typed
   protocol, the class *is* the convenience, not the mechanism.

Recognizing raw wrappers by class name
   Dispatching on names has precedent in pickle, which recognizes the
   ``__newobj__`` and ``__newobj_ex__`` callables in reduce tuples by
   their ``__name__``, but this PEP does not follow that example.
   Recognizing raw wrappers by ``type(o).__name__ == "RawJSON"`` would
   inspect an ordinary, plausible class name on arbitrary objects: any
   unrelated class that happens to use it would silently change
   encoding behavior.  The duck-typed ``__raw_json__`` keeps the
   marker in the reserved dunder namespace.

Consulting ``__raw_json__`` directly on all objects
   Would let a class self-serialize raw with a single method, but adds
   a type-attribute lookup for every object on the way to the subclass
   checks, to the duck-typed interpretations (``__index__``,
   ``__iter__``, etc.) and to ``default()`` — a real cost in one of
   the hottest paths in the standard library.  The gated design makes
   the raw object pay one method call instead.

Reusing ``__repr__``/``__str__`` or a generic ``__serialize__``
   Proposed in :cpython-issue:`114285`.  These methods cannot serve as
   the *marker* for raw output: nearly every type defines them (and
   their results are usually not valid JSON), so the encoder could not
   tell raw-capable objects apart; a format-agnostic ``__serialize__``
   likewise cannot say *which* format it produces.  ``__str__`` can,
   however, serve as the *payload* once a separate marker exists — see
   Open Issues.

MRO-based (isinstance) dispatch for the registry
   The registry uses exact-type matching like
   ``copyreg.dispatch_table``.  Subclass dispatch is the protocol's
   job: a base class defines ``__json__`` once and subclasses inherit
   it.  Exact matching keeps lookup one dict access and avoids MRO
   scans on the hot path.


Open Issues
===========

* **Dict keys.**  Neither the registry nor ``__json__`` applies to dict
  keys; requests exist (:cpython-issue:`63020`, :cpython-issue:`117391`,
  :cpython-issue:`85741`, :cpython-issue:`117592`).  A compatible future
  extension is to consult the hooks exactly where the ``keys must be
  str...`` :exc:`TypeError` is raised today (before the ``skipkeys``
  skip), so conforming keys pay nothing; the hook result would re-enter
  key normalization, and container or raw results would be rejected.
  Deferred from this PEP.

* **Cycle detection through hooks.**  A hook returning a fresh
  equal-but-not-identical cycle each call exhausts the recursion limit
  (:exc:`RecursionError`) rather than reporting ``Circular reference
  detected``.  Tracking hook results in the ``check_circular`` markers
  would close this at some cost to the hook path; deferred pending
  evidence it matters in practice.

* **__raw_json__ as a pure marker.**  An open alternative keeps
  ``__raw_json__`` as the marker but takes the emitted fragment from
  ``str(o)`` instead of the method's return value — ``RawJSON``
  defines ``__str__`` returning the fragment for exactly this reason,
  and the same would hold if raw wrappers were recognized by identity
  or name.  Calling the ``__str__`` slot is faster than a full method
  call, but the design would *require* every raw wrapper to define
  both ``__raw_json__`` and ``__str__``.


Appendix: Related issues
========================

The CPython issues referenced in this PEP, in chronological order:

* enum.IntEnum is not compatible with JSON serialisation
  (:cpython-issue:`62464`, closed)
* json.dumps() claims numpy.ndarray and numpy.bool\_ are not serializable
  (:cpython-issue:`62503`, closed)
* json.dump() ignores its 'default' option when serializing dictionary keys
  (:cpython-issue:`63020`, open)
* collections.deque should ship with a stdlib json serializer
  (:cpython-issue:`64973`, open)
* json library fails to serialize objects such as datetime
  (:cpython-issue:`65742`, closed)
* Only READ support for Decimal in json
  (:cpython-issue:`67312`, open)
* Allow namedtuple to be JSON encoded as dict
  (:cpython-issue:`67661`, closed)
* json fails to serialise numpy.int64
  (:cpython-issue:`68501`, closed)
* Serialize array.array to JSON by default
  (:cpython-issue:`70451`, open)
* json.dumps to check for obj.__json__ before raising TypeError
  (:cpython-issue:`71549`, open)
* Make collections.deque json serializable
  (:cpython-issue:`73849`, closed)
* Encode set, frozenset, bytearray, and iterators as json arrays
  (:cpython-issue:`75338`, closed)
* json int encoding incorrect for dbus.Byte
  (:cpython-issue:`77978`, closed)
* Json.dump() bug when using generator
  (:cpython-issue:`78031`, closed)
* MappingProxy objects should JSON serialize just like a dictionary
  (:cpython-issue:`79039`, open)
* Make Custom Object Classes JSON Serializable
  (:cpython-issue:`79292`, open)
* Supporting customization of float encoding in JSON
  (:cpython-issue:`81022`, open)
* json fails to encode dictionary view types
  (:cpython-issue:`83377`, closed)
* json.JSONEncoder.default should be called for dict keys as well
  (:cpython-issue:`85741`, closed)
* Introduce new data model method __iter_items__
  (:cpython-issue:`86931`, closed)
* There is no way to json encode object to str.
  (:cpython-issue:`86957`, open)
* Proper or custom JSON serialization of non-finite float values
  (:cpython-issue:`98306`, open)
* Json encode from __repr__, __str__ or __serialize__ when available
  (:cpython-issue:`114285`, closed)
* Allow JSONEncoder to handle passing unsupported dict keys through .default() before throwing TypeError
  (:cpython-issue:`117391`, open)
* json's default callable/method ignores keys.
  (:cpython-issue:`117592`, closed)
* Allow the JSON encoder to optionally support decimal.Decimal objects
  (:cpython-issue:`118810`, closed)
* Allow customization of NaN and Infinity serialization in json module
  (:cpython-issue:`134717`, closed)
* Optional Decimal to JSON Number Conversion in json Module
  (:cpython-issue:`145115`, closed)


.. _July 2010: https://mail.python.org/pipermail/python-ideas/2010-July/007811.html
.. _April 2020: https://mail.python.org/archives/list/python-ideas@python.org/thread/ISSUQVYI5OYYXKELUNCD5YCEDZ75LCEB/
.. _discuss.python.org in 2022–2024: https://discuss.python.org/t/introduce-a-json-magic-method/21768
.. _python-ideas in 2019: https://mail.python.org/archives/list/python-ideas@python.org/thread/WT6Z6YJDEZXKQ6OQLGAPB3OZ4OHCTPDU/
.. _discuss.python.org in 2022: https://discuss.python.org/t/json-register/20289
.. _TurboGears: https://turbogears.readthedocs.io/en/latest/cookbook/jsonp.html
.. _Pyramid's JSON renderer: https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/renderers.html
.. _simplejson: https://simplejson.readthedocs.io/
.. _simplejson issue #52: https://github.com/simplejson/simplejson/issues/52


Copyright
=========

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.
