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:

Made with contrib.rocks.

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 Any, Type, overload
 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: str | None = None,
118    reuse_instances_default: bool = True,
119    convert_sets_default: bool = False,
120    transparent: bool = False,
121    serializer: SerializeFunc | None = None,
122    deserializer: DeserializeFunc | None = None,
123    tagging: Tagging = DefaultTagging,
124    type_check: TypeCheck = strict,
125    serialize_class_var: bool = False,
126    class_serializer: ClassSerializer | None = None,
127    class_deserializer: ClassDeserializer | None = None,
128    deny_unknown_fields: bool = False,
129) -> Type[T]: ...
130
131
132@overload
133def serde(
134    _cls: Any = None,
135    rename_all: str | None = None,
136    reuse_instances_default: bool = True,
137    convert_sets_default: bool = False,
138    transparent: bool = False,
139    serializer: SerializeFunc | None = None,
140    deserializer: DeserializeFunc | None = None,
141    tagging: Tagging = DefaultTagging,
142    type_check: TypeCheck = strict,
143    serialize_class_var: bool = False,
144    class_serializer: ClassSerializer | None = None,
145    class_deserializer: ClassDeserializer | None = None,
146    deny_unknown_fields: bool = False,
147) -> Callable[[type[T]], type[T]]: ...
148
149
150@dataclass_transform(field_specifiers=(field,))
151def serde(
152    _cls: Any = None,
153    rename_all: str | None = None,
154    reuse_instances_default: bool = True,
155    convert_sets_default: bool = False,
156    transparent: bool = False,
157    serializer: SerializeFunc | None = None,
158    deserializer: DeserializeFunc | None = None,
159    tagging: Tagging = DefaultTagging,
160    type_check: TypeCheck = strict,
161    serialize_class_var: bool = False,
162    class_serializer: ClassSerializer | None = None,
163    class_deserializer: ClassDeserializer | None = None,
164    deny_unknown_fields: bool = False,
165) -> Any:
166    """
167    serde decorator. Keyword arguments are passed in `serialize` and `deserialize`.
168    """
169
170    def wrap(cls: Any) -> Any:
171        if should_impl_dataclass(cls):
172            dataclass(cls)
173        serialize(
174            cls,
175            rename_all=rename_all,
176            reuse_instances_default=reuse_instances_default,
177            convert_sets_default=convert_sets_default,
178            transparent=transparent,
179            serializer=serializer,
180            deserializer=deserializer,
181            tagging=tagging,
182            type_check=type_check,
183            serialize_class_var=serialize_class_var,
184            class_serializer=class_serializer,
185        )
186        deserialize(
187            cls,
188            rename_all=rename_all,
189            reuse_instances_default=reuse_instances_default,
190            convert_sets_default=convert_sets_default,
191            transparent=transparent,
192            serializer=serializer,
193            deserializer=deserializer,
194            tagging=tagging,
195            type_check=type_check,
196            serialize_class_var=serialize_class_var,
197            class_deserializer=class_deserializer,
198            deny_unknown_fields=deny_unknown_fields,
199        )
200        return cls
201
202    if _cls is None:
203        return wrap
204
205    return wrap(_cls)
@dataclass_transform(field_specifiers=(field,))
def serde( _cls: Any = None, rename_all: str | None = None, reuse_instances_default: bool = True, convert_sets_default: bool = False, transparent: bool = False, serializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = None, deserializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = 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: ClassSerializer | None = None, class_deserializer: ClassDeserializer | None = None, deny_unknown_fields: bool = False) -> Any:
151@dataclass_transform(field_specifiers=(field,))
152def serde(
153    _cls: Any = None,
154    rename_all: str | None = None,
155    reuse_instances_default: bool = True,
156    convert_sets_default: bool = False,
157    transparent: bool = False,
158    serializer: SerializeFunc | None = None,
159    deserializer: DeserializeFunc | None = None,
160    tagging: Tagging = DefaultTagging,
161    type_check: TypeCheck = strict,
162    serialize_class_var: bool = False,
163    class_serializer: ClassSerializer | None = None,
164    class_deserializer: ClassDeserializer | None = None,
165    deny_unknown_fields: bool = False,
166) -> Any:
167    """
168    serde decorator. Keyword arguments are passed in `serialize` and `deserialize`.
169    """
170
171    def wrap(cls: Any) -> Any:
172        if should_impl_dataclass(cls):
173            dataclass(cls)
174        serialize(
175            cls,
176            rename_all=rename_all,
177            reuse_instances_default=reuse_instances_default,
178            convert_sets_default=convert_sets_default,
179            transparent=transparent,
180            serializer=serializer,
181            deserializer=deserializer,
182            tagging=tagging,
183            type_check=type_check,
184            serialize_class_var=serialize_class_var,
185            class_serializer=class_serializer,
186        )
187        deserialize(
188            cls,
189            rename_all=rename_all,
190            reuse_instances_default=reuse_instances_default,
191            convert_sets_default=convert_sets_default,
192            transparent=transparent,
193            serializer=serializer,
194            deserializer=deserializer,
195            tagging=tagging,
196            type_check=type_check,
197            serialize_class_var=serialize_class_var,
198            class_deserializer=class_deserializer,
199            deny_unknown_fields=deny_unknown_fields,
200        )
201        return cls
202
203    if _cls is None:
204        return wrap
205
206    return wrap(_cls)

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

@dataclass_transform()
def serialize( _cls: type[~T] | None = None, rename_all: str | None = None, reuse_instances_default: bool = False, convert_sets_default: bool = False, serializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = 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, transparent: bool = False, class_serializer: ClassSerializer | None = None, **kwargs: Any) -> type[~T]:
196@dataclass_transform()
197def serialize(
198    _cls: type[T] | None = None,
199    rename_all: str | None = None,
200    reuse_instances_default: bool = False,
201    convert_sets_default: bool = False,
202    serializer: SerializeFunc | None = None,
203    tagging: Tagging = DefaultTagging,
204    type_check: TypeCheck = strict,
205    serialize_class_var: bool = False,
206    transparent: bool = False,
207    class_serializer: ClassSerializer | None = None,
208    **kwargs: Any,
209) -> type[T]:
210    """
211    A dataclass with this decorator is serializable into any of the data formats
212    supported by pyserde.
213
214    >>> from datetime import datetime
215    >>> from serde import serialize
216    >>> from serde.json import to_json
217    >>>
218    >>> @serialize
219    ... class Foo:
220    ...     i: int
221    ...     s: str
222    ...     f: float
223    ...     b: bool
224    >>>
225    >>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
226    '{"i":10,"s":"foo","f":100.0,"b":true}'
227    """
228
229    def wrap(cls: type[T]) -> type[T]:
230        tagging.check()
231
232        # If no `dataclass` found in the class, dataclassify it automatically.
233        if not is_dataclass(cls):
234            dataclass(cls)
235
236        if transparent:
237            get_transparent_field(cls)
238
239        if type_check.is_strict():
240            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
241            serde_beartype(cls)
242
243        g: dict[str, Any] = {}
244
245        # Create a scope storage used by serde.
246        # Each class should get own scope. Child classes can not share scope with parent class.
247        # That's why we need the "scope.cls is not cls" check.
248        scope: Scope | None = getattr(cls, SERDE_SCOPE, None)
249        if scope is None or scope.cls is not cls:
250            scope = Scope(
251                cls,
252                reuse_instances_default=reuse_instances_default,
253                convert_sets_default=convert_sets_default,
254            )
255            setattr(cls, SERDE_SCOPE, scope)
256        scope.transparent = transparent
257
258        class_serializers: list[ClassSerializer] = list(
259            itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else [])
260        )
261
262        # Set some globals for all generated functions
263        g["cls"] = cls
264        g["copy"] = copy
265        g["serde_scope"] = scope
266        g["SerdeError"] = SerdeError
267        g["enum_value"] = enum_value
268        g["is_dataclass"] = is_dataclass
269        g["typename"] = typename  # used in union functions
270        g["is_instance"] = is_instance  # used in union functions
271        g["to_obj"] = to_obj
272        g["typing"] = typing
273        g["Literal"] = Literal
274        g["TypeCheck"] = TypeCheck
275        g["disabled"] = disabled
276        g["coerce_object"] = coerce_object
277        g["class_serializers"] = class_serializers
278        if serializer:
279            g["serde_legacy_custom_class_serializer"] = functools.partial(
280                serde_legacy_custom_class_serializer, custom=serializer
281            )
282
283        # Collect types used in the generated code.
284        for typ in iter_types(cls):
285            # When we encounter a dataclass not marked with serialize, then also generate serialize
286            # functions for it.
287            if is_dataclass_without_se(typ) and typ is not cls:
288                # We call serialize and not wrap to make sure that we will use the default serde
289                # configuration for generating the serialization function.
290                serialize(typ)
291
292            if is_primitive(typ) and not is_enum(typ):
293                continue
294            g[typename(typ)] = typ
295
296        # render all union functions
297        for union in iter_unions(cls):
298            union_args = list(type_args(union))
299            union_key = union_func_name(UNION_SE_PREFIX, union_args)
300            add_func(scope, union_key, render_union_func(cls, union_args, tagging), g)
301            scope.union_se_args[union_key] = union_args
302
303        for f in sefields(cls, serialize_class_var):
304            if f.skip_if:
305                g[f.skip_if.name] = f.skip_if
306            if f.serializer:
307                g[f.serializer.name] = f.serializer
308
309        add_func(
310            scope,
311            TO_ITER,
312            render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer),
313            g,
314        )
315        add_func(
316            scope,
317            TO_DICT,
318            render_to_dict(
319                cls, rename_all, serializer, type_check, serialize_class_var, class_serializer
320            ),
321            g,
322        )
323
324        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
325
326        return cls
327
328    if _cls is None:
329        return wrap  # type: ignore
330
331    if _cls in GENERATION_STACK:
332        return _cls
333
334    GENERATION_STACK.append(_cls)
335    try:
336        return wrap(_cls)
337    finally:
338        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: type[~T] | None = None, rename_all: str | None = None, reuse_instances_default: bool = True, convert_sets_default: bool = False, deserializer: Callable[[type[typing.Any], typing.Any], typing.Any] | None = 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: ClassDeserializer | None = None, deny_unknown_fields: bool = False, transparent: bool = False, **kwargs: Any) -> type[~T]:
205@dataclass_transform()
206def deserialize(
207    _cls: type[T] | None = None,
208    rename_all: str | None = None,
209    reuse_instances_default: bool = True,
210    convert_sets_default: bool = False,
211    deserializer: DeserializeFunc | None = None,
212    tagging: Tagging = DefaultTagging,
213    type_check: TypeCheck = strict,
214    class_deserializer: ClassDeserializer | None = None,
215    deny_unknown_fields: bool = False,
216    transparent: bool = False,
217    **kwargs: Any,
218) -> type[T]:
219    """
220    A dataclass with this decorator is deserializable from any of the data formats supported
221    by pyserde.
222
223    >>> from serde import deserialize
224    >>> from serde.json import from_json
225    >>>
226    >>> @deserialize
227    ... class Foo:
228    ...     i: int
229    ...     s: str
230    ...     f: float
231    ...     b: bool
232    >>>
233    >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
234    Foo(i=10, s='foo', f=100.0, b=True)
235    """
236
237    stack = []
238
239    def wrap(cls: type[T]) -> type[T]:
240        if cls in stack:
241            return cls
242        stack.append(cls)
243
244        tagging.check()
245
246        # If no `dataclass` found in the class, dataclassify it automatically.
247        if not is_dataclass(cls):
248            dataclass(cls)
249
250        if transparent:
251            get_transparent_field(cls)
252
253        if type_check.is_strict():
254            serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError))
255            serde_beartype(cls)
256
257        g: dict[str, Any] = {}
258
259        # Create a scope storage used by serde.
260        # Each class should get own scope. Child classes can not share scope with parent class.
261        # That's why we need the "scope.cls is not cls" check.
262        scope: Scope | None = getattr(cls, SERDE_SCOPE, None)
263        if scope is None or scope.cls is not cls:
264            scope = Scope(
265                cls,
266                reuse_instances_default=reuse_instances_default,
267                convert_sets_default=convert_sets_default,
268            )
269            setattr(cls, SERDE_SCOPE, scope)
270        scope.transparent = transparent
271
272        class_deserializers: list[ClassDeserializer] = list(
273            itertools.chain(
274                GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else []
275            )
276        )
277
278        # Set some globals for all generated functions
279        g["cls"] = cls
280        g["serde_scope"] = scope
281        g["SerdeError"] = SerdeError
282        g["UserError"] = UserError
283        g["typename"] = typename
284        g["ensure"] = ensure
285        g["typing"] = typing
286        g["collections"] = collections
287        g["Literal"] = Literal
288        g["from_obj"] = from_obj
289        g["get_generic_arg"] = get_generic_arg
290        g["is_instance"] = is_instance
291        g["TypeCheck"] = TypeCheck
292        g["disabled"] = disabled
293        g["coerce_object"] = coerce_object
294        g["_exists_by_aliases"] = _exists_by_aliases
295        g["_get_by_aliases"] = _get_by_aliases
296        g["class_deserializers"] = class_deserializers
297        g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation
298        g["is_bearable"] = is_bearable
299        if deserializer:
300            g["serde_legacy_custom_class_deserializer"] = functools.partial(
301                serde_legacy_custom_class_deserializer, custom=deserializer
302            )
303
304        # Collect types used in the generated code.
305        for typ in iter_types(cls):
306            # When we encounter a dataclass not marked with deserialize, then also generate
307            # deserialize functions for it.
308            if is_dataclass_without_de(typ) and typ is not cls:
309                # We call deserialize and not wrap to make sure that we will use the default serde
310                # configuration for generating the deserialization function.
311                deserialize(typ)
312
313            # We don't want to add primitive class e.g "str" into the scope, but primitive
314            # compatible types such as IntEnum and a subclass of primitives are added,
315            # so that generated code can use those types.
316            if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ):
317                continue
318
319            if is_generic(typ):
320                g[typename(typ)] = get_origin(typ)
321            else:
322                g[typename(typ)] = typ
323
324        # render all union functions
325        for union in iter_unions(cls):
326            union_args = type_args(union)
327            add_func(
328                scope,
329                union_func_name(UNION_DE_PREFIX, union_args),
330                render_union_func(cls, union_args, tagging),
331                g,
332            )
333
334        # render literal functions
335        for literal in iter_literals(cls):
336            literal_args = type_args(literal)
337            add_func(
338                scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g
339            )
340
341        # Collect default values and default factories used in the generated code.
342        for f in defields(cls):
343            assert f.name
344            if has_default(f):
345                scope.defaults[f.name] = f.default
346            elif has_default_factory(f):
347                scope.defaults[f.name] = f.default_factory
348            if f.deserializer:
349                g[f.deserializer.name] = f.deserializer
350
351        add_func(
352            scope,
353            FROM_ITER,
354            render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer),
355            g,
356        )
357        add_func(
358            scope,
359            FROM_DICT,
360            render_from_dict(
361                cls,
362                rename_all,
363                deserializer,
364                type_check,
365                class_deserializer=class_deserializer,
366                deny_unknown_fields=deny_unknown_fields,
367            ),
368            g,
369        )
370
371        logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}")
372
373        stack.pop()
374        return cls
375
376    if _cls is None:
377        return wrap  # type: ignore
378
379    if _cls in GENERATION_STACK:
380        return _cls
381
382    GENERATION_STACK.append(_cls)
383    try:
384        return wrap(_cls)
385    finally:
386        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:
341def is_serializable(instance_or_class: Any) -> bool:
342    """
343    Test if an instance or class is serializable.
344
345    >>> @serialize
346    ... class Foo:
347    ...     pass
348
349    Testing `Foo` class object returns `True`.
350    >>> is_serializable(Foo)
351    True
352
353    Testing `Foo` object also returns `True`.
354    >>> is_serializable(Foo())
355    True
356    """
357    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:
389def is_deserializable(instance_or_class: Any) -> bool:
390    """
391    Test if an instance or class is deserializable.
392
393    >>> @deserialize
394    ... class Foo:
395    ...     pass
396    >>>
397    >>> is_deserializable(Foo)
398    True
399    """
400    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: type[typing.Any] | None = None, reuse_instances: bool | None = None, convert_sets: bool | None = None, skip_none: bool = False) -> dict[typing.Any, typing.Any]:
492def to_dict(
493    o: Any,
494    c: type[Any] | None = None,
495    reuse_instances: bool | None = None,
496    convert_sets: bool | None = None,
497    skip_none: bool = False,
498) -> dict[Any, Any]:
499    """
500    Serialize object into python dictionary. This function ensures that the dataclass's fields are
501    accurately represented as key-value pairs in the resulting dictionary.
502
503    * `o`: Any pyserde object that you want to convert to `dict`
504    * `c`: Optional class argument
505    * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer
506    instead of converting them to serializable representation e.g. string. This behaviour allows
507    to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially
508    improve performance.
509    * `convert_sets`: This option controls how sets are handled during serialization and
510    deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during
511    serialization and back to sets during deserialization. This is useful for data formats that
512    do not natively support sets.
513    * `skip_none`: When set to True, any field in the class with a None value is excluded from the
514    serialized output. Defaults to False.
515
516    >>> from serde import serde
517    >>> @serde
518    ... class Foo:
519    ...     i: int
520    ...     s: str = 'foo'
521    ...     f: float = 100.0
522    ...     b: bool = True
523    >>>
524    >>> to_dict(Foo(i=10))
525    {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}
526
527    You can serialize not only pyserde objects but also objects of any supported types. For example,
528    the following example serializes list of pyserde objects into dict.
529
530    >>> lst = [Foo(i=10), Foo(i=20)]
531    >>> to_dict(lst)
532    [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
533    """
534    return to_obj(  # type: ignore
535        o,
536        named=True,
537        c=c,
538        reuse_instances=reuse_instances,
539        convert_sets=convert_sets,
540        skip_none=skip_none,
541    )

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: bool | None = None, deserialize_numbers: Callable[[str | int], float] | None = None) -> Any:
589def from_dict(
590    cls: Any,
591    o: dict[str, Any],
592    reuse_instances: bool | None = None,
593    deserialize_numbers: Callable[[str | int], float] | None = None,
594) -> Any:
595    """
596    Deserialize dictionary into object.
597
598    >>> @deserialize
599    ... class Foo:
600    ...     i: int
601    ...     s: str = 'foo'
602    ...     f: float = 100.0
603    ...     b: bool = True
604    >>>
605    >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
606    Foo(i=10, s='foo', f=100.0, b=True)
607
608    You can pass any type supported by pyserde. For example,
609
610    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
611      type is float (e.g. accept ints or numeric strings supplied by a parser).
612
613    >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
614    ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
615    >>> from_dict(list[Foo], lst)
616    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
617    """
618    return from_obj(
619        cls,
620        o,
621        named=True,
622        reuse_instances=reuse_instances,
623        deserialize_numbers=deserialize_numbers,
624    )

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,

  • deserialize_numbers: Optional callable to coerce numeric input to floats when the target type is float (e.g. accept ints or numeric strings supplied by a parser).
>>> 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: type[typing.Any] | None = None, reuse_instances: bool | None = None, convert_sets: bool | None = None, skip_none: bool = False) -> tuple[typing.Any, ...]:
449def to_tuple(
450    o: Any,
451    c: type[Any] | None = None,
452    reuse_instances: bool | None = None,
453    convert_sets: bool | None = None,
454    skip_none: bool = False,
455) -> tuple[Any, ...]:
456    """
457    Serialize object into tuple.
458
459    >>> @serialize
460    ... class Foo:
461    ...     i: int
462    ...     s: str = 'foo'
463    ...     f: float = 100.0
464    ...     b: bool = True
465    >>>
466    >>> to_tuple(Foo(i=10))
467    (10, 'foo', 100.0, True)
468
469    You can pass any type supported by pyserde. For example,
470
471    >>> lst = [Foo(i=10), Foo(i=20)]
472    >>> to_tuple(lst)
473    [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
474    """
475    return to_obj(  # type: ignore
476        o,
477        named=False,
478        c=c,
479        reuse_instances=reuse_instances,
480        convert_sets=convert_sets,
481        skip_none=skip_none,
482    )

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: bool | None = None, deserialize_numbers: Callable[[str | int], float] | None = None) -> Any:
645def from_tuple(
646    cls: Any,
647    o: Any,
648    reuse_instances: bool | None = None,
649    deserialize_numbers: Callable[[str | int], float] | None = None,
650) -> Any:
651    """
652    Deserialize tuple into object.
653
654    >>> @deserialize
655    ... class Foo:
656    ...     i: int
657    ...     s: str = 'foo'
658    ...     f: float = 100.0
659    ...     b: bool = True
660    >>>
661    >>> from_tuple(Foo, (10, 'foo', 100.0, True))
662    Foo(i=10, s='foo', f=100.0, b=True)
663
664    You can pass any type supported by pyserde. For example,
665
666    * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target
667      type is float (e.g. accept ints or numeric strings supplied by a parser).
668
669    >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
670    >>> from_tuple(list[Foo], lst)
671    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
672    """
673    return from_obj(
674        cls,
675        o,
676        named=False,
677        reuse_instances=reuse_instances,
678        deserialize_numbers=deserialize_numbers,
679    )

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,

  • deserialize_numbers: Optional callable to coerce numeric input to floats when the target type is float (e.g. accept ints or numeric strings supplied by a parser).
>>> 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):
93class SerdeError(Exception):
94    """
95    Serde error class.
96    """

Serde error class.

class SerdeSkip(builtins.Exception):
108class SerdeSkip(Exception):
109    """
110    Skip a field in custom (de)serializer.
111    """

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]]:
948def AdjacentTagging(tag: str, content: str, cls: T | None = None) -> Tagging | _WithTagging[T]:
949    tagging = Tagging(tag, content, kind=Tagging.Kind.Adjacent)
950    if cls:
951        return tagging(cls)
952    else:
953        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]]:
932def InternalTagging(tag: str, cls: T | None = None) -> Tagging | _WithTagging[T]:
933    tagging = Tagging(tag, kind=Tagging.Kind.Internal)
934    if cls:
935        return tagging(cls)
936    else:
937        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: str | None = None, alias: list[str] | None = None, skip: bool | None = None, skip_if: Callable[[typing.Any], typing.Any] | None = None, skip_if_false: bool | None = None, skip_if_default: bool | None = None, serializer: Callable[..., typing.Any] | None = None, deserializer: Callable[..., typing.Any] | None = None, flatten: serde.core.FlattenOpts | bool | None = None, metadata: dict[str, typing.Any] | None = None, **kwargs: Any) -> Any:
567def field(
568    *args: Any,
569    rename: str | None = None,
570    alias: list[str] | None = None,
571    skip: bool | None = None,
572    skip_if: Callable[[Any], Any] | None = None,
573    skip_if_false: bool | None = None,
574    skip_if_default: bool | None = None,
575    serializer: Callable[..., Any] | None = None,
576    deserializer: Callable[..., Any] | None = None,
577    flatten: FlattenOpts | bool | None = None,
578    metadata: dict[str, Any] | None = None,
579    **kwargs: Any,
580) -> Any:
581    """
582    Declare a field with parameters.
583    """
584    if not metadata:
585        metadata = {}
586
587    if rename is not None:
588        metadata["serde_rename"] = rename
589    if alias is not None:
590        metadata["serde_alias"] = alias
591    if skip is not None:
592        metadata["serde_skip"] = skip
593    if skip_if is not None:
594        metadata["serde_skip_if"] = skip_if
595    if skip_if_false is not None:
596        metadata["serde_skip_if_false"] = skip_if_false
597    if skip_if_default is not None:
598        metadata["serde_skip_if_default"] = skip_if_default
599    if serializer:
600        metadata["serde_serializer"] = serializer
601    if deserializer:
602        metadata["serde_deserializer"] = deserializer
603    if flatten is True:
604        metadata["serde_flatten"] = FlattenOpts()
605    elif flatten:
606        metadata["serde_flatten"] = flatten
607
608    return dataclasses.field(*args, metadata=metadata, **kwargs)

Declare a field with parameters.

def default_deserializer(_cls: type[typing.Any], obj: Any) -> Any:
144def default_deserializer(_cls: type[Any], obj: Any) -> Any:
145    """
146    Marker function to tell serde to use the default deserializer. It's used when custom
147    deserializer is specified at the class but you want to override a field with the default
148    deserializer.
149    """

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]:
485def asdict(v: Any) -> dict[Any, Any]:
486    """
487    Serialize object into dictionary.
488    """
489    return to_dict(v, reuse_instances=False, convert_sets=False)

Serialize object into dictionary.

def astuple(v: Any) -> tuple[typing.Any, ...]:
442def astuple(v: Any) -> tuple[Any, ...]:
443    """
444    Serialize object into tuple.
445    """
446    return to_tuple(v, reuse_instances=False, convert_sets=False)

Serialize object into tuple.

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

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:
100def init(debug: bool = False) -> None:
101    SETTINGS["debug"] = debug
logger = <Logger serde (WARNING)>
class ClassSerializer(typing.Protocol):
1095class ClassSerializer(Protocol):
1096    """
1097    Interface for custom class serializer.
1098
1099    This protocol is intended to be used for custom class serializer.
1100
1101    >>> from datetime import datetime
1102    >>> from serde import serde
1103    >>> from plum import dispatch
1104    >>> class MySerializer(ClassSerializer):
1105    ...     @dispatch
1106    ...     def serialize(self, value: datetime) -> str:
1107    ...         return value.strftime("%d/%m/%y")
1108    """
1109
1110    def serialize(self, value: Any) -> Any:
1111        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)
1771def _no_init_or_replace_init(self, *args, **kwargs):
1772    cls = type(self)
1773
1774    if cls._is_protocol:
1775        raise TypeError('Protocols cannot be instantiated')
1776
1777    # Already using a custom `__init__`. No need to calculate correct
1778    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1779    if cls.__init__ is not _no_init_or_replace_init:
1780        return
1781
1782    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1783    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1784    # searches for a proper new `__init__` in the MRO. The new `__init__`
1785    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1786    # instantiation of the protocol subclass will thus use the new
1787    # `__init__` and no longer call `_no_init_or_replace_init`.
1788    for base in cls.__mro__:
1789        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1790        if init is not _no_init_or_replace_init:
1791            cls.__init__ = init
1792            break
1793    else:
1794        # should not happen
1795        cls.__init__ = object.__init__
1796
1797    cls.__init__(self, *args, **kwargs)
def serialize(self, value: Any) -> Any:
1110    def serialize(self, value: Any) -> Any:
1111        pass
class ClassDeserializer(typing.Protocol):
1114class ClassDeserializer(Protocol):
1115    """
1116    Interface for custom class deserializer.
1117
1118    This protocol is intended to be used for custom class deserializer.
1119
1120    >>> from datetime import datetime
1121    >>> from serde import serde
1122    >>> from plum import dispatch
1123    >>> class MyDeserializer(ClassDeserializer):
1124    ...     @dispatch
1125    ...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
1126    ...         return datetime.strptime(value, "%d/%m/%y")
1127    """
1128
1129    def deserialize(self, cls: Any, value: Any) -> Any:
1130        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)
1771def _no_init_or_replace_init(self, *args, **kwargs):
1772    cls = type(self)
1773
1774    if cls._is_protocol:
1775        raise TypeError('Protocols cannot be instantiated')
1776
1777    # Already using a custom `__init__`. No need to calculate correct
1778    # `__init__` to call. This can lead to RecursionError. See bpo-45121.
1779    if cls.__init__ is not _no_init_or_replace_init:
1780        return
1781
1782    # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`.
1783    # The first instantiation of the subclass will call `_no_init_or_replace_init` which
1784    # searches for a proper new `__init__` in the MRO. The new `__init__`
1785    # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent
1786    # instantiation of the protocol subclass will thus use the new
1787    # `__init__` and no longer call `_no_init_or_replace_init`.
1788    for base in cls.__mro__:
1789        init = base.__dict__.get('__init__', _no_init_or_replace_init)
1790        if init is not _no_init_or_replace_init:
1791            cls.__init__ = init
1792            break
1793    else:
1794        # should not happen
1795        cls.__init__ = object.__init__
1796
1797    cls.__init__(self, *args, **kwargs)
def deserialize(self, cls: Any, value: Any) -> Any:
1129    def deserialize(self, cls: Any, value: Any) -> Any:
1130        pass
def add_serializer(serializer: ClassSerializer) -> None:
1138def add_serializer(serializer: ClassSerializer) -> None:
1139    """
1140    Register custom global serializer.
1141    """
1142    GLOBAL_CLASS_SERIALIZER.append(serializer)

Register custom global serializer.

def add_deserializer(deserializer: ClassDeserializer) -> None:
1145def add_deserializer(deserializer: ClassDeserializer) -> None:
1146    """
1147    Register custom global deserializer.
1148    """
1149    GLOBAL_CLASS_DESERIALIZER.append(deserializer)

Register custom global deserializer.