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

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:
535def from_dict(cls: Any, o: dict[str, Any], reuse_instances: Optional[bool] = None) -> Any:
536    """
537    Deserialize dictionary into object.
538
539    >>> @deserialize
540    ... class Foo:
541    ...     i: int
542    ...     s: str = 'foo'
543    ...     f: float = 100.0
544    ...     b: bool = True
545    >>>
546    >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
547    Foo(i=10, s='foo', f=100.0, b=True)
548
549    You can pass any type supported by pyserde. For example,
550
551    >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
552    ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
553    >>> from_dict(list[Foo], lst)
554    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
555    """
556    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, ...]:
427def to_tuple(
428    o: Any,
429    c: Optional[type[Any]] = None,
430    reuse_instances: Optional[bool] = None,
431    convert_sets: Optional[bool] = None,
432    skip_none: bool = False,
433) -> tuple[Any, ...]:
434    """
435    Serialize object into tuple.
436
437    >>> @serialize
438    ... class Foo:
439    ...     i: int
440    ...     s: str = 'foo'
441    ...     f: float = 100.0
442    ...     b: bool = True
443    >>>
444    >>> to_tuple(Foo(i=10))
445    (10, 'foo', 100.0, True)
446
447    You can pass any type supported by pyserde. For example,
448
449    >>> lst = [Foo(i=10), Foo(i=20)]
450    >>> to_tuple(lst)
451    [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
452    """
453    return to_obj(  # type: ignore
454        o,
455        named=False,
456        c=c,
457        reuse_instances=reuse_instances,
458        convert_sets=convert_sets,
459        skip_none=skip_none,
460    )

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:
567def from_tuple(cls: Any, o: Any, reuse_instances: Optional[bool] = None) -> Any:
568    """
569    Deserialize tuple into object.
570
571    >>> @deserialize
572    ... class Foo:
573    ...     i: int
574    ...     s: str = 'foo'
575    ...     f: float = 100.0
576    ...     b: bool = True
577    >>>
578    >>> from_tuple(Foo, (10, 'foo', 100.0, True))
579    Foo(i=10, s='foo', f=100.0, b=True)
580
581    You can pass any type supported by pyserde. For example,
582
583    >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
584    >>> from_tuple(list[Foo], lst)
585    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
586    """
587    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):
83class SerdeError(Exception):
84    """
85    Serde error class.
86    """

Serde error class.

class SerdeSkip(builtins.Exception):
 98class SerdeSkip(Exception):
 99    """
100    Skip a field in custom (de)serializer.
101    """

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

Declare a field with parameters.

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

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]:
463def asdict(v: Any) -> dict[Any, Any]:
464    """
465    Serialize object into dictionary.
466    """
467    return to_dict(v, reuse_instances=False, convert_sets=False)

Serialize object into dictionary.

def astuple(v: Any) -> tuple[typing.Any, ...]:
420def astuple(v: Any) -> tuple[Any, ...]:
421    """
422    Serialize object into tuple.
423    """
424    return to_tuple(v, reuse_instances=False, convert_sets=False)

Serialize object into tuple.

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

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:
90def init(debug: bool = False) -> None:
91    SETTINGS["debug"] = debug
logger = <Logger serde (WARNING)>
class ClassSerializer(typing.Protocol):
1001class ClassSerializer(Protocol):
1002    """
1003    Interface for custom class serializer.
1004
1005    This protocol is intended to be used for custom class serializer.
1006
1007    >>> from datetime import datetime
1008    >>> from serde import serde
1009    >>> from plum import dispatch
1010    >>> class MySerializer(ClassSerializer):
1011    ...     @dispatch
1012    ...     def serialize(self, value: datetime) -> str:
1013    ...         return value.strftime("%d/%m/%y")
1014    """
1015
1016    def serialize(self, value: Any) -> Any:
1017        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:
1016    def serialize(self, value: Any) -> Any:
1017        pass
class ClassDeserializer(typing.Protocol):
1020class ClassDeserializer(Protocol):
1021    """
1022    Interface for custom class deserializer.
1023
1024    This protocol is intended to be used for custom class deserializer.
1025
1026    >>> from datetime import datetime
1027    >>> from serde import serde
1028    >>> from plum import dispatch
1029    >>> class MyDeserializer(ClassDeserializer):
1030    ...     @dispatch
1031    ...     def deserialize(self, cls: type[datetime], value: Any) -> datetime:
1032    ...         return datetime.strptime(value, "%d/%m/%y")
1033    """
1034
1035    def deserialize(self, cls: Any, value: Any) -> Any:
1036        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:
1035    def deserialize(self, cls: Any, value: Any) -> Any:
1036        pass
def add_serializer(serializer: ClassSerializer) -> None:
1044def add_serializer(serializer: ClassSerializer) -> None:
1045    """
1046    Register custom global serializer.
1047    """
1048    GLOBAL_CLASS_SERIALIZER.append(serializer)

Register custom global serializer.

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

Register custom global deserializer.