mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-08 09:34:08 +02:00
Add Database(json_default=, json_object_hook=) for custom JSON encoding/decoding
Values not natively serializable (set, enum.Enum, project-specific types) previously always fell back to repr() when written to a JSON column, with no way to reconstruct them on read. json_default is passed through to json.dumps() as default= wherever the library serializes dict/list/tuple values; json_object_hook is passed through to json.loads() as object_hook= when deserialize_json=True. Both are Database-level, matching the existing deserialize_json option, since every write/read path already threads through self.db. Fixes #521
This commit is contained in:
parent
064625da59
commit
816028b169
3 changed files with 125 additions and 13 deletions
|
|
@ -2302,6 +2302,56 @@ any string value that looks like a JSON object or array back into a ``dict`` or
|
|||
This defaults to ``False``, so existing code that expects raw strings continues to work
|
||||
unchanged.
|
||||
|
||||
.. _python_api_custom_json:
|
||||
|
||||
Custom JSON encoding and decoding
|
||||
----------------------------------
|
||||
|
||||
By default, values that are not natively JSON-serializable - such as
|
||||
``set``, ``enum.Enum`` members or custom classes - are converted using
|
||||
``repr()`` when they are written to a JSON column, since ``dict``, ``list``
|
||||
and ``tuple`` values are always serialized with ``json.dumps()``. To take
|
||||
full control of that conversion, pass a ``json_default`` function to the
|
||||
``Database()`` constructor - it is used as the ``default=`` callback to
|
||||
``json.dumps()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import enum
|
||||
|
||||
class Color(enum.Enum):
|
||||
RED = "red"
|
||||
BLUE = "blue"
|
||||
|
||||
def encode(value):
|
||||
if isinstance(value, set):
|
||||
return list(value)
|
||||
if isinstance(value, enum.Enum):
|
||||
return value.value
|
||||
raise TypeError(f"Cannot serialize {value!r}")
|
||||
|
||||
db = sqlite_utils.Database("data.db", json_default=encode)
|
||||
db["examples"].insert({"id": 1, "tags": {"a", "b"}, "color": Color.RED})
|
||||
|
||||
To reconstruct custom types when reading rows back with
|
||||
``deserialize_json=True``, pass a ``json_object_hook`` function - it is
|
||||
used as the ``object_hook=`` callback to ``json.loads()`` and is called
|
||||
with every JSON object (``dict``) encountered while parsing:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def decode(obj):
|
||||
if "__color__" in obj:
|
||||
return Color(obj["__color__"])
|
||||
return obj
|
||||
|
||||
db = sqlite_utils.Database(
|
||||
"data.db", deserialize_json=True, json_object_hook=decode
|
||||
)
|
||||
|
||||
Both options apply to every table on that ``Database`` connection - there is
|
||||
no per-table equivalent, matching how ``deserialize_json`` works.
|
||||
|
||||
.. _python_api_conversions:
|
||||
|
||||
Converting column values using SQL functions
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue