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

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:
519def from_dict(cls: Any, o: dict[str, Any], reuse_instances: Optional[bool] = None) -> Any:
520    """
521    Deserialize dictionary into object.
522
523    >>> @deserialize
524    ... class Foo:
525    ...     i: int
526    ...     s: str = 'foo'
527    ...     f: float = 100.0
528    ...     b: bool = True
529    >>>
530    >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
531    Foo(i=10, s='foo', f=100.0, b=True)
532
533    You can pass any type supported by pyserde. For example,
534
535    >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
536    ...        {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
537    >>> from_dict(list[Foo], lst)
538    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
539    """
540    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, ...]:
407def to_tuple(
408    o: Any,
409    c: Optional[type[Any]] = None,
410    reuse_instances: Optional[bool] = None,
411    convert_sets: Optional[bool] = None,
412    skip_none: bool = False,
413) -> tuple[Any, ...]:
414    """
415    Serialize object into tuple.
416
417    >>> @serialize
418    ... class Foo:
419    ...     i: int
420    ...     s: str = 'foo'
421    ...     f: float = 100.0
422    ...     b: bool = True
423    >>>
424    >>> to_tuple(Foo(i=10))
425    (10, 'foo', 100.0, True)
426
427    You can pass any type supported by pyserde. For example,
428
429    >>> lst = [Foo(i=10), Foo(i=20)]
430    >>> to_tuple(lst)
431    [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
432    """
433    return to_obj(  # type: ignore
434        o,
435        named=False,
436        c=c,
437        reuse_instances=reuse_instances,
438        convert_sets=convert_sets,
439        skip_none=skip_none,
440    )

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:
551def from_tuple(cls: Any, o: Any, reuse_instances: Optional[bool] = None) -> Any:
552    """
553    Deserialize tuple into object.
554
555    >>> @deserialize
556    ... class Foo:
557    ...     i: int
558    ...     s: str = 'foo'
559    ...     f: float = 100.0
560    ...     b: bool = True
561    >>>
562    >>> from_tuple(Foo, (10, 'foo', 100.0, True))
563    Foo(i=10, s='foo', f=100.0, b=True)
564
565    You can pass any type supported by pyserde. For example,
566
567    >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
568    >>> from_tuple(list[Foo], lst)
569    [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
570    """
571    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:
125def default_deserializer(_cls: type[Any], obj: Any) -> Any:
126    """
127    Marker function to tell serde to use the default deserializer. It's used when custom
128    deserializer is specified at the class but you want to override a field with the default
129    deserializer.
130    """

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]:
443def asdict(v: Any) -> dict[Any, Any]:
444    """
445    Serialize object into dictionary.
446    """
447    return to_dict(v, reuse_instances=False, convert_sets=False)

Serialize object into dictionary.

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

Serialize object into tuple.

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

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.