Edit on GitHub

serde

pyserde

Yet another serialization library on top of dataclasses, inspired by serde-rs.

pypi pypi GithubActions CodeCov

GuideπŸ‡¬πŸ‡§ | γ‚¬γ‚€γƒ‰πŸ‡―πŸ‡΅ | API Reference | Examples

Overview

pyserde is a simple yet powerful serialization library on top of dataclasses. It allows you to convert Python objects to and from JSON, YAML, and other formats easily and efficiently.

Declare your class with @serde decorator and annotate fields using PEP484 as below.

@serde
class Foo:
    i: int
    s: str
    f: float
    b: bool

You can serialize Foo object into JSON.

>>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
'{"i":10,"s":"foo","f":100.0,"b":true}'

You can deserialize JSON into Foo object.

>>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
Foo(i=10, s='foo', f=100.0, b=True)

That's it! If you're interested in pyserde, please check our documentation! Happy coding with pyserde! πŸš€

Features

Extensions

Contributors ✨

Thanks goes to these wonderful people (emoji key):

yukinarit
yukinarit

πŸ’»
Alexander Miskaryan
Alexander Miskaryan

πŸ’»
ydylla
ydylla

πŸ’»
Kevin Squire
Kevin Squire

πŸ’» πŸ“–
Yushi OMOTE
Yushi OMOTE

πŸ’»
Yuji Kanagawa
Yuji Kanagawa

πŸ’»
Weiliang Li
Weiliang Li

πŸ’»
Mauve
Mauve

πŸ’»
adsharma
adsharma

πŸ’»
Guilhem C.
Guilhem C.

πŸ“–
Pierre Tardy
Pierre Tardy

πŸ’»
Raphael Nestler
Raphael Nestler

πŸ“–
Pranav V P
Pranav V P

πŸ’»
andreymal
andreymal

πŸ’»
Johann Fuechsl
Johann Fuechsl

πŸ’»
DoeringChristian
DoeringChristian

πŸ’»
Stuart Axelbrooke
Stuart Axelbrooke

πŸ’»
Jakub BerΓ‘nek
Jakub BerΓ‘nek

πŸ’»
Fredrik Reinholdsen
Fredrik Reinholdsen

πŸ’»
Bruno Oliveira
Bruno Oliveira

πŸ“–
Kyle Kosic
Kyle Kosic

πŸ’»
Gajo Petrovic
Gajo Petrovic

πŸ“–
m472
m472

πŸ’»
acolley-gel
acolley-gel

πŸ’»
Marc-AndrΓ© Allaire
Marc-AndrΓ© Allaire

πŸ’»
Ganden Schaffner
Ganden Schaffner

πŸ’»
Dave Tapley
Dave Tapley

πŸ’»
Beartama
Beartama

πŸ’»
Rachael Sexton
Rachael Sexton

πŸ’»
JWSong
JWSong

πŸ’»
Emanuele Barsanti
Emanuele Barsanti

πŸ’»
Aman Clement Aranha
Aman Clement Aranha

πŸ“–
𝕂'
𝕂'

πŸ“–
Add your contributions

This project follows the all-contributors specification. Contributions of any kind welcome!

LICENSE

This project is licensed under the MIT license.

Modules

The following modules provide the core functionalities of pyserde.

The following modules provide pyserde's (de)serialize APIs.

Other modules

  1"""
  2.. include:: ../README.md
  3
  4## Modules
  5
  6The following modules provide the core functionalities of `pyserde`.
  7* `serde.se`: All about serialization.
  8* `serde.de`: All about deserialization.
  9* `serde.core`: Core module used by `serde.se` and `serde.de` modules.
 10* `serde.compat`: Compatibility layer which handles mostly differences of `typing` module between
 11python versions.
 12
 13The following modules provide pyserde's (de)serialize APIs.
 14* `serde.json`: Serialize and Deserialize in JSON.
 15* `serde.msgpack`: Serialize and Deserialize in MsgPack.
 16* `serde.yaml`: Serialize and Deserialize in YAML.
 17* `serde.toml`: Serialize and Deserialize in TOML.
 18* `serde.pickle`: Serialize and Deserialize in Pickle.
 19
 20Other modules
 21* `serde.inspect`: Prints generated code by pyserde.
 22"""
 23
 24from dataclasses import dataclass
 25from collections.abc import Callable
 26from typing import Optional, overload, Any, Type
 27
 28from typing_extensions import dataclass_transform
 29
 30from .compat import SerdeError, SerdeSkip, T
 31from .core import (
 32    ClassSerializer,
 33    ClassDeserializer,
 34    AdjacentTagging,
 35    coerce,
 36    DefaultTagging,
 37    ExternalTagging,
 38    InternalTagging,
 39    disabled,
 40    strict,
 41    Tagging,
 42    TypeCheck,
 43    Untagged,
 44    field,
 45    init,
 46    logger,
 47    should_impl_dataclass,
 48    add_serializer,
 49    add_deserializer,
 50)
 51from .de import (
 52    DeserializeFunc,
 53    default_deserializer,
 54    deserialize,
 55    from_dict,
 56    from_tuple,
 57    is_deserializable,
 58)
 59from .se import (
 60    SerializeFunc,
 61    asdict,
 62    astuple,
 63    default_serializer,
 64    is_serializable,
 65    serialize,
 66    to_dict,
 67    to_tuple,
 68)
 69
 70__all__ = [
 71    "serde",
 72    "serialize",
 73    "deserialize",
 74    "is_serializable",
 75    "is_deserializable",
 76    "to_dict",
 77    "from_dict",
 78    "to_tuple",
 79    "from_tuple",
 80    "SerdeError",
 81    "SerdeSkip",
 82    "AdjacentTagging",
 83    "ExternalTagging",
 84    "InternalTagging",
 85    "Untagged",
 86    "disabled",
 87    "strict",
 88    "coerce",
 89    "field",
 90    "default_deserializer",
 91    "asdict",
 92    "astuple",
 93    "default_serializer",
 94    "compat",
 95    "core",
 96    "de",
 97    "inspect",
 98    "json",
 99    "msgpack",
100    "numpy",
101    "se",
102    "toml",
103    "pickle",
104    "yaml",
105    "init",
106    "logger",
107    "ClassSerializer",
108    "ClassDeserializer",
109    "add_serializer",
110    "add_deserializer",
111]
112
113
114@overload
115def serde(
116    _cls: Type[T],
117    rename_all: Optional[str] = None,
118    reuse_instances_default: bool = True,
119    convert_sets_default: bool = False,
120    serializer: Optional[SerializeFunc] = None,
121    deserializer: Optional[DeserializeFunc] = None,
122    tagging: Tagging = DefaultTagging,
123    type_check: TypeCheck = strict,
124    serialize_class_var: bool = False,
125    class_serializer: Optional[ClassSerializer] = None,
126    class_deserializer: Optional[ClassDeserializer] = None,
127    deny_unknown_fields: bool = False,
128) -> Type[T]: ...
129
130
131@overload
132def serde(
133    _cls: Any = None,
134    rename_all: Optional[str] = None,
135    reuse_instances_default: bool = True,
136    convert_sets_default: bool = False,
137    serializer: Optional[SerializeFunc] = None,
138    deserializer: Optional[DeserializeFunc] = None,
139    tagging: Tagging = DefaultTagging,
140    type_check: TypeCheck = strict,
141    serialize_class_var: bool = False,
142    class_serializer: Optional[ClassSerializer] = None,
143    class_deserializer: Optional[ClassDeserializer] = None,
144    deny_unknown_fields: bool = False,
145) -> Callable[[type[T]], type[T]]: ...
146
147
148@dataclass_transform(field_specifiers=(field,))
149def serde(
150    _cls: Any = None,
151    rename_all: Optional[str] = None,
152    reuse_instances_default: bool = True,
153    convert_sets_default: bool = False,
154    serializer: Optional[SerializeFunc] = None,
155    deserializer: Optional[DeserializeFunc] = None,
156    tagging: Tagging = DefaultTagging,
157    type_check: TypeCheck = strict,
158    serialize_class_var: bool = False,
159    class_serializer: Optional[ClassSerializer] = None,
160    class_deserializer: Optional[ClassDeserializer] = None,
161    deny_unknown_fields: bool = False,
162) -> Any:
163    """
164    serde decorator. Keyword arguments are passed in `serialize` and `deserialize`.
165    """
166
167    def wrap(cls: Any) -> Any:
168        if should_impl_dataclass(cls):
169            dataclass(cls)
170        serialize(
171            cls,
172            rename_all=rename_all,
173            reuse_instances_default=reuse_instances_default,
174            convert_sets_default=convert_sets_default,
175            serializer=serializer,
176            deserializer=deserializer,
177            tagging=tagging,
178            type_check=type_check,
179            serialize_class_var=serialize_class_var,
180            class_serializer=class_serializer,
181        )
182        deserialize(
183            cls,
184            rename_all=rename_all,
185            reuse_instances_default=reuse_instances_default,
186            convert_sets_default=convert_sets_default,
187            serializer=serializer,
188            deserializer=deserializer,
189            tagging=tagging,
190            type_check=type_check,
191            serialize_class_var=serialize_class_var,
192            class_deserializer=class_deserializer,
193            deny_unknown_fields=deny_unknown_fields,
194        )
195        return cls
196
197    if _cls is None:
198        return wrap
199
200    return wrap(_cls)
@dataclass_transform(field_specifiers=(field,))
def serde( _cls: Any = None, rename_all: Optional[str] = None, reuse_instances_default: bool = True, convert_sets_default: bool = False, serializer: Optional[Callable[[type[Any], Any], Any]] = None, deserializer: Optional[Callable[[type[Any], Any], Any]] = None, tagging: serde.core.Tagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>), type_check: serde.core.TypeCheck = TypeCheck(kind=<Kind.Strict: 3>), serialize_class_var: bool = False, class_serializer: Optional[ClassSerializer] = None, class_deserializer: Optional[ClassDeserializer] = None, deny_unknown_fields: bool = False) -> Any:
149@dataclass_transform(field_specifiers=(field,))
150def serde(
151    _cls: Any = None,
152    rename_all: Optional[str] = None,
153    reuse_instances_default: bool = True,
154    convert_sets_default: bool = False,
155    serializer: Optional[SerializeFunc] = None,
156    deserializer: Optional[DeserializeFunc] = None,
157    tagging: Tagging = DefaultTagging,
158    type_check: TypeCheck = strict,
159    serialize_class_var: bool = False,
160    class_serializer: Optional[ClassSerializer] = None,
161    class_deserializer: Optional[ClassDeserializer] = None,
162    deny_unknown_fields: bool = False,
163) -> Any:
164    """
165    serde decorator. Keyword arguments are passed in `serialize` and `deserialize`.
166    """
167
168    def wrap(cls: Any) -> Any:
169        if should_impl_dataclass(cls):
170            dataclass(cls)
171        serialize(
172            cls,
173            rename_all=rename_all,
174            reuse_instances_default=reuse_instances_default,
175            convert_sets_default=convert_sets_default,
176            serializer=serializer,
177            deserializer=deserializer,
178            tagging=tagging,
179            type_check=type_check,
180            serialize_class_var=serialize_class_var,
181            class_serializer=class_serializer,
182        )
183        deserialize(
184            cls,
185            rename_all=rename_all,
186            reuse_instances_default=reuse_instances_default,
187            convert_sets_default=convert_sets_default,
188            serializer=serializer,
189            deserializer=deserializer,
190            tagging=tagging,
191            type_check=type_check,
192            serialize_class_var=serialize_class_var,
193            class_deserializer=class_deserializer,
194            deny_unknown_fields=deny_unknown_fields,
195        )
196        return cls
197
198    if _cls is None:
199        return wrap
200
201    return wrap(_cls)

serde decorator. Keyword arguments are passed in serialize and deserialize.

@dataclass_transform()
def serialize( _cls: Optional[type[~T]] = None, rename_all: Optional[str] = None, reuse_instances_default: bool = False, convert_sets_default: bool = False, serializer: Optional[Callable[[type[Any], Any], Any]] = None, tagging: serde.core.Tagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>), type_check: serde.core.TypeCheck = TypeCheck(kind=<Kind.Strict: 3>), serialize_class_var: bool = False, class_serializer: Optional[ClassSerializer] = None, **kwargs: Any) -> type[~T]:
163@dataclass_transform()
164def serialize(
165    _cls: Optional[type[T]] = None,
166    rename_all: Optional[str] = None,
167    reuse_instances_default: bool = False,
168    convert_sets_default: bool = False,
169    serializer: Optional[SerializeFunc] = None,
170    tagging: Tagging = DefaultTagging,
171    type_check: TypeCheck = strict,
172    serialize_class_var: bool = False,
173    class_serializer: Optional[ClassSerializer] = None,
174    **kwargs: Any,
175) -> type[T]:
176    """
177    A dataclass with this decorator is serializable into any of the data formats
178    supported by pyserde.
179
180    >>> from datetime import datetime
181    >>> from serde import serialize
182    >>> from serde.json import to_json
183    >>>
184    >>> @serialize
185    ... class Foo:
186    ...     i: int
187    ...     s: str
188    ...     f: float
189    ...     b: bool
190    >>>
191    >>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
192    '{"i":10,"s":"foo","f":100.0,"b":true}'
193    """
194
195    def wrap(cls: type[T]) -> type[T]:
196        tagging.check()
197
198        # If no `dataclass` found in the class, dataclassify it automatically.
199        if not is_dataclass(cls):
200            dataclass(cls)
201
202        if type_check.is_strict():
203            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
204            serde_beartype(cls)
205
206        g: dict[str, Any] = {}
207
208        # Create a scope storage used by serde.
209        # Each class should get own scope. Child classes can not share scope with parent class.
210        # That's why we need the "scope.cls is not cls" check.
211        scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None)
212        if scope is None or scope.cls is not cls:
213            scope = Scope(
214                cls,
215                reuse_instances_default=reuse_instances_default,
216                convert_sets_default=convert_sets_default,
217            )
218            setattr(cls, SERDE_SCOPE, scope)
219
220        class_serializers: list[ClassSerializer] = list(
221            itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else [])
222        )
223
224        # Set some globals for all generated functions
225        g["cls"] = cls
226        g["copy"] = copy
227        g["serde_scope"] = scope
228        g["SerdeError"] = SerdeError
229        g["raise_unsupported_type"] = raise_unsupported_type
230        g["enum_value"] = enum_value
231        g["is_dataclass"] = is_dataclass
232        g["typename"] = typename  # used in union functions
233        g["is_instance"] = is_instance  # used in union functions
234        g["to_obj"] = to_obj
235        g["typing"] = typing
236        g["Literal"] = Literal
237        g["TypeCheck"] = TypeCheck
238        g["disabled"] = disabled
239        g["coerce_object"] = coerce_object
240        g["class_serializers"] = class_serializers
241        if serializer:
242            g["serde_legacy_custom_class_serializer"] = functools.partial(
243                serde_legacy_custom_class_serializer, custom=serializer
244            )
245
246        # Collect types used in the generated code.
247        for typ in iter_types(cls):
248            # When we encounter a dataclass not marked with serialize, then also generate serialize
249            # functions for it.
250            if is_dataclass_without_se(typ) and typ is not cls:
251                # We call serialize and not wrap to make sure that we will use the default serde
252                # configuration for generating the serialization function.
253                serialize(typ)
254
255            if is_primitive(typ) and not is_enum(typ):
256                continue
257            g[typename(typ)] = typ
258
259        # render all union functions
260        for union in iter_unions(cls):
261            union_args = list(type_args(union))
262            union_key = union_func_name(UNION_SE_PREFIX, union_args)
263            add_func(scope, union_key, render_union_func(cls, union_args, tagging), g)
264            scope.union_se_args[union_key] = union_args
265
266        for f in sefields(cls, serialize_class_var):
267            if f.skip_if:
268                g[f.skip_if.name] = f.skip_if
269            if f.serializer:
270                g[f.serializer.name] = f.serializer
271
272        add_func(
273            scope,
274            TO_ITER,
275            render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer),
276            g,
277        )
278        add_func(
279            scope,
280            TO_DICT,
281            render_to_dict(
282                cls, rename_all, serializer, type_check, serialize_class_var, class_serializer
283            ),
284            g,
285        )
286
287        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
288
289        return cls
290
291    if _cls is None:
292        return wrap  # type: ignore
293
294    if _cls in GENERATION_STACK:
295        return _cls
296
297    GENERATION_STACK.append(_cls)
298    try:
299        return wrap(_cls)
300    finally:
301        GENERATION_STACK.pop()

A dataclass with this decorator is serializable into any of the data formats supported by pyserde.

>>> from datetime import datetime
>>> from serde import serialize
>>> from serde.json import to_json
>>>
>>> @serialize
... class Foo:
...     i: int
...     s: str
...     f: float
...     b: bool
>>>
>>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
'{"i":10,"s":"foo","f":100.0,"b":true}'
@dataclass_transform()
def deserialize( _cls: Optional[type[~T]] = None, rename_all: Optional[str] = None, reuse_instances_default: bool = True, convert_sets_default: bool = False, deserializer: Optional[Callable[[type[Any], Any], Any]] = None, tagging: serde.core.Tagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>), type_check: serde.core.TypeCheck = TypeCheck(kind=<Kind.Strict: 3>), class_deserializer: Optional[ClassDeserializer] = None, deny_unknown_fields: bool = False, **kwargs: Any) -> type[~T]:
187@dataclass_transform()
188def deserialize(
189    _cls: Optional[type[T]] = None,
190    rename_all: Optional[str] = None,
191    reuse_instances_default: bool = True,
192    convert_sets_default: bool = False,
193    deserializer: Optional[DeserializeFunc] = None,
194    tagging: Tagging = DefaultTagging,
195    type_check: TypeCheck = strict,
196    class_deserializer: Optional[ClassDeserializer] = None,
197    deny_unknown_fields: bool = False,
198    **kwargs: Any,
199) -> type[T]:
200    """
201    A dataclass with this decorator is deserializable from any of the data formats supported
202    by pyserde.
203
204    >>> from serde import deserialize
205    >>> from serde.json import from_json
206    >>>
207    >>> @deserialize
208    ... class Foo:
209    ...     i: int
210    ...     s: str
211    ...     f: float
212    ...     b: bool
213    >>>
214    >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
215    Foo(i=10, s='foo', f=100.0, b=True)
216    """
217
218    stack = []
219
220    def wrap(cls: type[T]) -> type[T]:
221        if cls in stack:
222            return cls
223        stack.append(cls)
224
225        tagging.check()
226
227        # If no `dataclass` found in the class, dataclassify it automatically.
228        if not is_dataclass(cls):
229            dataclass(cls)
230
231        if type_check.is_strict():
232            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
233            serde_beartype(cls)
234
235        g: dict[str, Any] = {}
236
237        # Create a scope storage used by serde.
238        # Each class should get own scope. Child classes can not share scope with parent class.
239        # That's why we need the "scope.cls is not cls" check.
240        scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None)
241        if scope is None or scope.cls is not cls:
242            scope = Scope(cls, reuse_instances_default=reuse_instances_default)
243            setattr(cls, SERDE_SCOPE, scope)
244
245        class_deserializers: list[ClassDeserializer] = list(
246            itertools.chain(
247                GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else []
248            )
249        )
250
251        # Set some globals for all generated functions
252        g["cls"] = cls
253        g["serde_scope"] = scope
254        g["SerdeError"] = SerdeError
255        g["UserError"] = UserError
256        g["raise_unsupported_type"] = raise_unsupported_type
257        g["typename"] = typename
258        g["ensure"] = ensure
259        g["typing"] = typing
260        g["collections"] = collections
261        g["Literal"] = Literal
262        g["from_obj"] = from_obj
263        g["get_generic_arg"] = get_generic_arg
264        g["is_instance"] = is_instance
265        g["TypeCheck"] = TypeCheck
266        g["disabled"] = disabled
267        g["coerce_object"] = coerce_object
268        g["_exists_by_aliases"] = _exists_by_aliases
269        g["_get_by_aliases"] = _get_by_aliases
270        g["class_deserializers"] = class_deserializers
271        g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation
272        g["is_bearable"] = is_bearable
273        if deserializer:
274            g["serde_legacy_custom_class_deserializer"] = functools.partial(
275                serde_legacy_custom_class_deserializer, custom=deserializer
276            )
277
278        # Collect types used in the generated code.
279        for typ in iter_types(cls):
280            # When we encounter a dataclass not marked with deserialize, then also generate
281            # deserialize functions for it.
282            if is_dataclass_without_de(typ) and typ is not cls:
283                # We call deserialize and not wrap to make sure that we will use the default serde
284                # configuration for generating the deserialization function.
285                deserialize(typ)
286
287            # We don't want to add primitive class e.g "str" into the scope, but primitive
288            # compatible types such as IntEnum and a subclass of primitives are added,
289            # so that generated code can use those types.
290            if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ):
291                continue
292
293            if is_generic(typ):
294                g[typename(typ)] = get_origin(typ)
295            else:
296                g[typename(typ)] = typ
297
298        # render all union functions
299        for union in iter_unions(cls):
300            union_args = type_args(union)
301            add_func(
302                scope,
303                union_func_name(UNION_DE_PREFIX, union_args),
304                render_union_func(cls, union_args, tagging),
305                g,
306            )
307
308        # render literal functions
309        for literal in iter_literals(cls):
310            literal_args = type_args(literal)
311            add_func(
312                scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g
313            )
314
315        # Collect default values and default factories used in the generated code.
316        for f in defields(cls):
317            assert f.name
318            if has_default(f):
319                scope.defaults[f.name] = f.default
320            elif has_default_factory(f):
321                scope.defaults[f.name] = f.default_factory
322            if f.deserializer:
323                g[f.deserializer.name] = f.deserializer
324
325        add_func(
326            scope,
327            FROM_ITER,
328            render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer),
329            g,
330        )
331        add_func(
332            scope,
333            FROM_DICT,
334            render_from_dict(
335                cls,
336                rename_all,
337                deserializer,
338                type_check,
339                class_deserializer=class_deserializer,
340                deny_unknown_fields=deny_unknown_fields,
341            ),
342            g,
343        )
344
345        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
346
347        stack.pop()
348        return cls
349
350    if _cls is None:
351        return wrap  # type: ignore
352
353    if _cls in GENERATION_STACK:
354        return _cls
355
356    GENERATION_STACK.append(_cls)
357    try:
358        return wrap(_cls)
359    finally:
360        GENERATION_STACK.pop()

A dataclass with this decorator is deserializable from any of the data formats supported by pyserde.

>>> from serde import deserialize
>>> from serde.json import from_json
>>>
>>> @deserialize
... class Foo:
...     i: int
...     s: str
...     f: float
...     b: bool
>>>
>>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
Foo(i=10, s='foo', f=100.0, b=True)
def is_serializable(instance_or_class: Any) -> bool:
304def is_serializable(instance_or_class: Any) -> bool:
305    """
306    Test if an instance or class is serializable.
307
308    >>> @serialize
309    ... class Foo:
310    ...     pass
311
312    Testing `Foo` class object returns `True`.
313    >>> is_serializable(Foo)
314    True
315
316    Testing `Foo` object also returns `True`.
317    >>> is_serializable(Foo())
318    True
319    """
320    return hasattr(instance_or_class, SERDE_SCOPE)

Test if an instance or class is serializable.

>>> @serialize
... class Foo:
...     pass

Testing Foo class object returns True.

>>> is_serializable(Foo)
True

Testing Foo object also returns True.

>>> is_serializable(Foo())
True
def is_deserializable(instance_or_class: Any) -> bool:
363def is_deserializable(instance_or_class: Any) -> bool:
364    """
365    Test if an instance or class is deserializable.
366
367    >>> @deserialize
368    ... class Foo:
369    ...     pass
370    >>>
371    >>> is_deserializable(Foo)
372    True
373    """
374    return hasattr(instance_or_class, SERDE_SCOPE)

Test if an instance or class is deserializable.

>>> @deserialize
... class Foo:
...     pass
>>>
>>> is_deserializable(Foo)
True
def to_dict( o: Any, c: Optional[type[Any]] = None, reuse_instances: Optional[bool] = None, convert_sets: Optional[bool] = None, skip_none: bool = False) -> dict[typing.Any, typing.Any]:
451def to_dict(
452    o: Any,
453    c: Optional[type[Any]] = None,
454    reuse_instances: Optional[bool] = None,
455    convert_sets: Optional[bool] = None,
456    skip_none: bool = False,
457) -> dict[Any, Any]:
458    """
459    Serialize object into python dictionary. This function ensures that the dataclass's fields are
460    accurately represented as key-value pairs in the resulting dictionary.
461
462    * `o`: Any pyserde object that you want to convert to `dict`
463    * `c`: Optional class argument
464    * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer
465    instead of converting them to serializable representation e.g. string. This behaviour allows
466    to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially
467    improve performance.
468    * `convert_sets`: This option controls how sets are handled during serialization and
469    deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during
470    serialization and back to sets during deserialization. This is useful for data formats that
471    do not natively support sets.
472    * `skip_none`: When set to True, any field in the class with a None value is excluded from the
473    serialized output. Defaults to False.
474
475    >>> from serde import serde
476    >>> @serde
477    ... class Foo:
478    ...     i: int
479    ...     s: str = 'foo'
480    ...     f: float = 100.0
481    ...     b: bool = True
482    >>>
483    >>> to_dict(Foo(i=10))
484    {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}
485
486    You can serialize not only pyserde objects but also objects of any supported types. For example,
487    the following example serializes list of pyserde objects into dict.
488
489    >>> lst = [Foo(i=10), Foo(i=20)]
490    >>> to_dict(lst)
491    [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
492    """
493    return to_obj(  # type: ignore
494        o,
495        named=True,
496        c=c,
497        reuse_instances=reuse_instances,
498        convert_sets=convert_sets,
499        skip_none=skip_none,
500    )

Serialize object into python dictionary. This function ensures that the dataclass's fields are accurately represented as key-value pairs in the resulting dictionary.

  • o: Any pyserde object that you want to convert to dict
  • c: Optional class argument
  • reuse_instances: pyserde will pass instances (e.g. Path, datetime) directly to serializer instead of converting them to serializable representation e.g. string. This behaviour allows to delegate serializtation to underlying data format packages e.g. pyyaml and potentially improve performance.
  • convert_sets: This option controls how sets are handled during serialization and deserialization. When convert_sets is set to True, pyserde will convert sets to lists during serialization and back to sets during deserialization. This is useful for data formats that do not natively support sets.
  • skip_none: When set to True, any field in the class with a None value is excluded from the serialized output. Defaults to False.
>>> from serde import serde
>>> @serde
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> to_dict(Foo(i=10))
{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}

You can serialize not only pyserde objects but also objects of any supported types. For example, the following example serializes list of pyserde objects into dict.

>>> lst = [Foo(i=10), Foo(i=20)]
>>> to_dict(lst)
[{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
def from_dict( cls: Any, o: dict[str, typing.Any], reuse_instances: Optional[bool] = None) -> Any:
520def from_dict(cls: Any, o: dict[str, Any], reuse_instances: Optional[bool] = None) -> Any:
521    """
522    Deserialize dictionary into object.
523
524    >>> @deserialize
525    ... class Foo:
526    ...     i: int
527    ...     s: str = 'foo'
528    ...     f: float = 100.0
529    ...     b: bool = True
530    >>>
531    >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
532    Foo(i=10, s='foo', f=100.0, b=True)
533
534    You can pass any type supported by pyserde. For example,
535
536    >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
537    ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
538    >>> from_dict(list[Foo], lst)
539    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
540    """
541    return from_obj(cls, o, named=True, reuse_instances=reuse_instances)

Deserialize dictionary into object.

>>> @deserialize
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
Foo(i=10, s='foo', f=100.0, b=True)

You can pass any type supported by pyserde. For example,

>>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
>>> from_dict(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
def to_tuple( o: Any, c: Optional[type[Any]] = None, reuse_instances: Optional[bool] = None, convert_sets: Optional[bool] = None, skip_none: bool = False) -> tuple[typing.Any, ...]:
408def to_tuple(
409    o: Any,
410    c: Optional[type[Any]] = None,
411    reuse_instances: Optional[bool] = None,
412    convert_sets: Optional[bool] = None,
413    skip_none: bool = False,
414) -> tuple[Any, ...]:
415    """
416    Serialize object into tuple.
417
418    >>> @serialize
419    ... class Foo:
420    ...     i: int
421    ...     s: str = 'foo'
422    ...     f: float = 100.0
423    ...     b: bool = True
424    >>>
425    >>> to_tuple(Foo(i=10))
426    (10, 'foo', 100.0, True)
427
428    You can pass any type supported by pyserde. For example,
429
430    >>> lst = [Foo(i=10), Foo(i=20)]
431    >>> to_tuple(lst)
432    [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
433    """
434    return to_obj(  # type: ignore
435        o,
436        named=False,
437        c=c,
438        reuse_instances=reuse_instances,
439        convert_sets=convert_sets,
440        skip_none=skip_none,
441    )

Serialize object into tuple.

>>> @serialize
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> to_tuple(Foo(i=10))
(10, 'foo', 100.0, True)

You can pass any type supported by pyserde. For example,

>>> lst = [Foo(i=10), Foo(i=20)]
>>> to_tuple(lst)
[(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
def from_tuple(cls: Any, o: Any, reuse_instances: Optional[bool] = None) -> Any:
552def from_tuple(cls: Any, o: Any, reuse_instances: Optional[bool] = None) -> Any:
553    """
554    Deserialize tuple into object.
555
556    >>> @deserialize
557    ... class Foo:
558    ...     i: int
559    ...     s: str = 'foo'
560    ...     f: float = 100.0
561    ...     b: bool = True
562    >>>
563    >>> from_tuple(Foo, (10, 'foo', 100.0, True))
564    Foo(i=10, s='foo', f=100.0, b=True)
565
566    You can pass any type supported by pyserde. For example,
567
568    >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
569    >>> from_tuple(list[Foo], lst)
570    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
571    """
572    return from_obj(cls, o, named=False, reuse_instances=reuse_instances)

Deserialize tuple into object.

>>> @deserialize
... class Foo:
...     i: int
...     s: str = 'foo'
...     f: float = 100.0
...     b: bool = True
>>>
>>> from_tuple(Foo, (10, 'foo', 100.0, True))
Foo(i=10, s='foo', f=100.0, b=True)

You can pass any type supported by pyserde. For example,

>>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
>>> from_tuple(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
class SerdeError(builtins.Exception):
76class SerdeError(Exception):
77    """
78    Serde error class.
79    """

Serde error class.

class SerdeSkip(builtins.Exception):
91class SerdeSkip(Exception):
92    """
93    Skip a field in custom (de)serializer.
94    """

Skip a field in custom (de)serializer.

def AdjacentTagging( tag: str, content: str, cls: Optional[~T] = None) -> Union[serde.core.Tagging, serde.compat._WithTagging[~T]]:
851def AdjacentTagging(
852    tag: str, content: str, cls: Optional[T] = None
853) -> Union[Tagging, _WithTagging[T]]:
854    tagging = Tagging(tag, content, kind=Tagging.Kind.Adjacent)
855    if cls:
856        return tagging(cls)
857    else:
858        return tagging
ExternalTagging = Tagging(tag=None, content=None, kind=<Kind.External: 1>)
def InternalTagging( tag: str, cls: Optional[~T] = None) -> Union[serde.core.Tagging, serde.compat._WithTagging[~T]]:
835def InternalTagging(tag: str, cls: Optional[T] = None) -> Union[Tagging, _WithTagging[T]]:
836    tagging = Tagging(tag, kind=Tagging.Kind.Internal)
837    if cls:
838        return tagging(cls)
839    else:
840        return tagging
Untagged = Tagging(tag=None, content=None, kind=<Kind.Untagged: 4>)
disabled = TypeCheck(kind=<Kind.Disabled: 1>)
strict = TypeCheck(kind=<Kind.Strict: 3>)
coerce = TypeCheck(kind=<Kind.Coerce: 2>)
def field( *args: Any, rename: Optional[str] = None, alias: Optional[list[str]] = None, skip: Optional[bool] = None, skip_if: Optional[Callable[[Any], Any]] = None, skip_if_false: Optional[bool] = None, skip_if_default: Optional[bool] = None, serializer: Optional[Callable[..., Any]] = None, deserializer: Optional[Callable[..., Any]] = None, flatten: Union[serde.core.FlattenOpts, bool, NoneType] = None, metadata: Optional[dict[str, Any]] = None, **kwargs: Any) -> Any:
515def field(
516    *args: Any,
517    rename: Optional[str] = None,
518    alias: Optional[list[str]] = None,
519    skip: Optional[bool] = None,
520    skip_if: Optional[Callable[[Any], Any]] = None,
521    skip_if_false: Optional[bool] = None,
522    skip_if_default: Optional[bool] = None,
523    serializer: Optional[Callable[..., Any]] = None,
524    deserializer: Optional[Callable[..., Any]] = None,
525    flatten: Optional[Union[FlattenOpts, bool]] = None,
526    metadata: Optional[dict[str, Any]] = None,
527    **kwargs: Any,
528) -> Any:
529    """
530    Declare a field with parameters.
531    """
532    if not metadata:
533        metadata = {}
534
535    if rename is not None:
536        metadata["serde_rename"] = rename
537    if alias is not None:
538        metadata["serde_alias"] = alias
539    if skip is not None:
540        metadata["serde_skip"] = skip
541    if skip_if is not None:
542        metadata["serde_skip_if"] = skip_if
543    if skip_if_false is not None:
544        metadata["serde_skip_if_false"] = skip_if_false
545    if skip_if_default is not None:
546        metadata["serde_skip_if_default"] = skip_if_default
547    if serializer:
548        metadata["serde_serializer"] = serializer
549    if deserializer:
550        metadata["serde_deserializer"] = deserializer
551    if flatten is True:
552        metadata["serde_flatten"] = FlattenOpts()
553    elif flatten:
554        metadata["serde_flatten"] = flatten
555
556    return dataclasses.field(*args, metadata=metadata, **kwargs)

Declare a field with parameters.

def default_deserializer(_cls: type[typing.Any], obj: Any) -> Any:
126def default_deserializer(_cls: type[Any], obj: Any) -> Any:
127    """
128    Marker function to tell serde to use the default deserializer. It's used when custom
129    deserializer is specified at the class but you want to override a field with the default
130    deserializer.
131    """

Marker function to tell serde to use the default deserializer. It's used when custom deserializer is specified at the class but you want to override a field with the default deserializer.

def asdict(v: Any) -> dict[typing.Any, typing.Any]:
444def asdict(v: Any) -> dict[Any, Any]:
445    """
446    Serialize object into dictionary.
447    """
448    return to_dict(v, reuse_instances=False, convert_sets=False)

Serialize object into dictionary.

def astuple(v: Any) -> tuple[typing.Any, ...]:
401def astuple(v: Any) -> tuple[Any, ...]:
402    """
403    Serialize object into tuple.
404    """
405    return to_tuple(v, reuse_instances=False, convert_sets=False)

Serialize object into tuple.

def default_serializer(_cls: type[typing.Any], obj: Any) -> Any:
 96def default_serializer(_cls: type[Any], obj: Any) -> Any:
 97    """
 98    Marker function to tell serde to use the default serializer. It's used when custom serializer
 99    is specified at the class but you want to override a field with the default serializer.
100    """

Marker function to tell serde to use the default serializer. It's used when custom serializer is specified at the class but you want to override a field with the default serializer.

def init(debug: bool = False) -> None:
89def init(debug: bool = False) -> None:
90    SETTINGS["debug"] = debug
logger = <Logger serde (WARNING)>
class ClassSerializer(typing.Protocol):
1000class ClassSerializer(Protocol):
1001    """
1002    Interface for custom class serializer.
1003
1004    This protocol is intended to be used for custom class serializer.
1005
1006    >>> from datetime import datetime
1007    >>> from serde import serde
1008    >>> from plum import dispatch
1009    >>> class MySerializer(ClassSerializer):
1010    ...     @dispatch
1011    ...     def serialize(self, value: datetime) -> str:
1012    ...         return value.strftime("%d/%m/%y")
1013    """
1014
1015    def serialize(self, value: Any) -> Any:
1016        pass

Interface for custom class serializer.

This protocol is intended to be used for custom class serializer.

>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MySerializer(ClassSerializer):
...     @dispatch
...     def serialize(self, value: datetime) -> str:
...         return value.strftime("%d/%m/%y")
ClassSerializer(*args, **kwargs)
1767def _no_init_or_replace_init(self, *args, **kwargs):
1768    cls = type(self)
1769
1770    if cls._is_protocol:
1771        raise TypeError('Protocols cannot be instantiated')
1772
1773    # Already using a custom `__init__`. No need to calculate correct
1774    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1775    if cls.__init__ is not _no_init_or_replace_init:
1776        return
1777
1778    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1779    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1780    # searches for a proper new `__init__` in the MRO. The new `__init__`
1781    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1782    # instantiation of the protocol subclass will thus use the new
1783    # `__init__` and no longer call `_no_init_or_replace_init`.
1784    for base in cls.__mro__:
1785        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1786        if init is not _no_init_or_replace_init:
1787            cls.__init__ = init
1788            break
1789    else:
1790        # should not happen
1791        cls.__init__ = object.__init__
1792
1793    cls.__init__(self, *args, **kwargs)
def serialize(self, value: Any) -> Any:
1015    def serialize(self, value: Any) -> Any:
1016        pass
class ClassDeserializer(typing.Protocol):
1019class ClassDeserializer(Protocol):
1020    """
1021    Interface for custom class deserializer.
1022
1023    This protocol is intended to be used for custom class deserializer.
1024
1025    >>> from datetime import datetime
1026    >>> from serde import serde
1027    >>> from plum import dispatch
1028    >>> class MyDeserializer(ClassDeserializer):
1029    ...     @dispatch
1030    ...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
1031    ...         return datetime.strptime(value, "%d/%m/%y")
1032    """
1033
1034    def deserialize(self, cls: Any, value: Any) -> Any:
1035        pass

Interface for custom class deserializer.

This protocol is intended to be used for custom class deserializer.

>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MyDeserializer(ClassDeserializer):
...     @dispatch
...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
...         return datetime.strptime(value, "%d/%m/%y")
ClassDeserializer(*args, **kwargs)
1767def _no_init_or_replace_init(self, *args, **kwargs):
1768    cls = type(self)
1769
1770    if cls._is_protocol:
1771        raise TypeError('Protocols cannot be instantiated')
1772
1773    # Already using a custom `__init__`. No need to calculate correct
1774    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1775    if cls.__init__ is not _no_init_or_replace_init:
1776        return
1777
1778    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1779    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1780    # searches for a proper new `__init__` in the MRO. The new `__init__`
1781    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1782    # instantiation of the protocol subclass will thus use the new
1783    # `__init__` and no longer call `_no_init_or_replace_init`.
1784    for base in cls.__mro__:
1785        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1786        if init is not _no_init_or_replace_init:
1787            cls.__init__ = init
1788            break
1789    else:
1790        # should not happen
1791        cls.__init__ = object.__init__
1792
1793    cls.__init__(self, *args, **kwargs)
def deserialize(self, cls: Any, value: Any) -> Any:
1034    def deserialize(self, cls: Any, value: Any) -> Any:
1035        pass
def add_serializer(serializer: ClassSerializer) -> None:
1043def add_serializer(serializer: ClassSerializer) -> None:
1044    """
1045    Register custom global serializer.
1046    """
1047    GLOBAL_CLASS_SERIALIZER.append(serializer)

Register custom global serializer.

def add_deserializer(deserializer: ClassDeserializer) -> None:
1050def add_deserializer(deserializer: ClassDeserializer) -> None:
1051    """
1052    Register custom global deserializer.
1053    """
1054    GLOBAL_CLASS_DESERIALIZER.append(deserializer)

Register custom global deserializer.