serde
pyserde
Yet another serialization library on top of dataclasses, inspired by serde-rs.
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
- Supported data formats
- dict
- tuple
- JSON
- Yaml
- Toml
- MsgPack
- Pickle
- Supported types
- Primitives (
int
,float
,str
,bool
) - Containers
list
,set
,tuple
,dict
frozenset
,defaultdict
typing.Optional
typing.Union
- User defined class with
@dataclass
typing.NewType
for primitive typestyping.Any
typing.Literal
typing.Generic
typing.ClassVar
dataclasses.InitVar
Enum
andIntEnum
- Standard library
- PyPI library
numpy
typesSQLAlchemy
Declarative Dataclass Mapping (experimental)
- Primitives (
- Class Attributes
- Field Attributes
- Decorators
- Type Check
- Union Representation
- Forward reference
- PEP563 Postponed Evaluation of Annotations
- PEP585 Type Hinting Generics In Standard Collections
- PEP604 Allow writing union types as X | Y
- PEP681 Data Class Transform
- PEP695 Type Parameter Syntax
- Case Conversion
- Rename
- Alias
- Skip (de)serialization (skip, skip_if, skip_if_false, skip_if_default)
- Custom field (de)serializer
- Custom class (de)serializer
- Custom global (de)serializer
- Flatten
Extensions
- pyserde-timedelta: (de)serializing datetime.timedelta in ISO 8601 duration format.
Contributors β¨
Thanks goes to these wonderful people (emoji key):
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
.
serde.se
: All about serialization.serde.de
: All about deserialization.serde.core
: Core module used byserde.se
andserde.de
modules.serde.compat
: Compatibility layer which handles mostly differences oftyping
module between python versions.
The following modules provide pyserde's (de)serialize APIs.
serde.json
: Serialize and Deserialize in JSON.serde.msgpack
: Serialize and Deserialize in MsgPack.serde.yaml
: Serialize and Deserialize in YAML.serde.toml
: Serialize and Deserialize in TOML.serde.pickle
: Serialize and Deserialize in Pickle.
Other modules
serde.inspect
: Prints generated code by pyserde.
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)
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
.
163@dataclass_transform() 164def serialize( 165 _cls: Optional[type[T]] = None, 166 rename_all: Optional[str] = None, 167 reuse_instances_default: bool = False, 168 convert_sets_default: bool = False, 169 serializer: Optional[SerializeFunc] = None, 170 tagging: Tagging = DefaultTagging, 171 type_check: TypeCheck = strict, 172 serialize_class_var: bool = False, 173 class_serializer: Optional[ClassSerializer] = None, 174 **kwargs: Any, 175) -> type[T]: 176 """ 177 A dataclass with this decorator is serializable into any of the data formats 178 supported by pyserde. 179 180 >>> from datetime import datetime 181 >>> from serde import serialize 182 >>> from serde.json import to_json 183 >>> 184 >>> @serialize 185 ... class Foo: 186 ... i: int 187 ... s: str 188 ... f: float 189 ... b: bool 190 >>> 191 >>> to_json(Foo(i=10, s='foo', f=100.0, b=True)) 192 '{"i":10,"s":"foo","f":100.0,"b":true}' 193 """ 194 195 def wrap(cls: type[T]) -> type[T]: 196 tagging.check() 197 198 # If no `dataclass` found in the class, dataclassify it automatically. 199 if not is_dataclass(cls): 200 dataclass(cls) 201 202 if type_check.is_strict(): 203 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 204 serde_beartype(cls) 205 206 g: dict[str, Any] = {} 207 208 # Create a scope storage used by serde. 209 # Each class should get own scope. Child classes can not share scope with parent class. 210 # That's why we need the "scope.cls is not cls" check. 211 scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None) 212 if scope is None or scope.cls is not cls: 213 scope = Scope( 214 cls, 215 reuse_instances_default=reuse_instances_default, 216 convert_sets_default=convert_sets_default, 217 ) 218 setattr(cls, SERDE_SCOPE, scope) 219 220 class_serializers: list[ClassSerializer] = list( 221 itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else []) 222 ) 223 224 # Set some globals for all generated functions 225 g["cls"] = cls 226 g["copy"] = copy 227 g["serde_scope"] = scope 228 g["SerdeError"] = SerdeError 229 g["raise_unsupported_type"] = raise_unsupported_type 230 g["enum_value"] = enum_value 231 g["is_dataclass"] = is_dataclass 232 g["typename"] = typename # used in union functions 233 g["is_instance"] = is_instance # used in union functions 234 g["to_obj"] = to_obj 235 g["typing"] = typing 236 g["Literal"] = Literal 237 g["TypeCheck"] = TypeCheck 238 g["disabled"] = disabled 239 g["coerce_object"] = coerce_object 240 g["class_serializers"] = class_serializers 241 if serializer: 242 g["serde_legacy_custom_class_serializer"] = functools.partial( 243 serde_legacy_custom_class_serializer, custom=serializer 244 ) 245 246 # Collect types used in the generated code. 247 for typ in iter_types(cls): 248 # When we encounter a dataclass not marked with serialize, then also generate serialize 249 # functions for it. 250 if is_dataclass_without_se(typ) and typ is not cls: 251 # We call serialize and not wrap to make sure that we will use the default serde 252 # configuration for generating the serialization function. 253 serialize(typ) 254 255 if is_primitive(typ) and not is_enum(typ): 256 continue 257 g[typename(typ)] = typ 258 259 # render all union functions 260 for union in iter_unions(cls): 261 union_args = list(type_args(union)) 262 union_key = union_func_name(UNION_SE_PREFIX, union_args) 263 add_func(scope, union_key, render_union_func(cls, union_args, tagging), g) 264 scope.union_se_args[union_key] = union_args 265 266 for f in sefields(cls, serialize_class_var): 267 if f.skip_if: 268 g[f.skip_if.name] = f.skip_if 269 if f.serializer: 270 g[f.serializer.name] = f.serializer 271 272 add_func( 273 scope, 274 TO_ITER, 275 render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer), 276 g, 277 ) 278 add_func( 279 scope, 280 TO_DICT, 281 render_to_dict( 282 cls, rename_all, serializer, type_check, serialize_class_var, class_serializer 283 ), 284 g, 285 ) 286 287 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 288 289 return cls 290 291 if _cls is None: 292 return wrap # type: ignore 293 294 if _cls in GENERATION_STACK: 295 return _cls 296 297 GENERATION_STACK.append(_cls) 298 try: 299 return wrap(_cls) 300 finally: 301 GENERATION_STACK.pop()
A dataclass with this decorator is serializable into any of the data formats supported by pyserde.
>>> from datetime import datetime
>>> from serde import serialize
>>> from serde.json import to_json
>>>
>>> @serialize
... class Foo:
... i: int
... s: str
... f: float
... b: bool
>>>
>>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
'{"i":10,"s":"foo","f":100.0,"b":true}'
187@dataclass_transform() 188def deserialize( 189 _cls: Optional[type[T]] = None, 190 rename_all: Optional[str] = None, 191 reuse_instances_default: bool = True, 192 convert_sets_default: bool = False, 193 deserializer: Optional[DeserializeFunc] = None, 194 tagging: Tagging = DefaultTagging, 195 type_check: TypeCheck = strict, 196 class_deserializer: Optional[ClassDeserializer] = None, 197 deny_unknown_fields: bool = False, 198 **kwargs: Any, 199) -> type[T]: 200 """ 201 A dataclass with this decorator is deserializable from any of the data formats supported 202 by pyserde. 203 204 >>> from serde import deserialize 205 >>> from serde.json import from_json 206 >>> 207 >>> @deserialize 208 ... class Foo: 209 ... i: int 210 ... s: str 211 ... f: float 212 ... b: bool 213 >>> 214 >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}') 215 Foo(i=10, s='foo', f=100.0, b=True) 216 """ 217 218 stack = [] 219 220 def wrap(cls: type[T]) -> type[T]: 221 if cls in stack: 222 return cls 223 stack.append(cls) 224 225 tagging.check() 226 227 # If no `dataclass` found in the class, dataclassify it automatically. 228 if not is_dataclass(cls): 229 dataclass(cls) 230 231 if type_check.is_strict(): 232 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 233 serde_beartype(cls) 234 235 g: dict[str, Any] = {} 236 237 # Create a scope storage used by serde. 238 # Each class should get own scope. Child classes can not share scope with parent class. 239 # That's why we need the "scope.cls is not cls" check. 240 scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None) 241 if scope is None or scope.cls is not cls: 242 scope = Scope(cls, reuse_instances_default=reuse_instances_default) 243 setattr(cls, SERDE_SCOPE, scope) 244 245 class_deserializers: list[ClassDeserializer] = list( 246 itertools.chain( 247 GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else [] 248 ) 249 ) 250 251 # Set some globals for all generated functions 252 g["cls"] = cls 253 g["serde_scope"] = scope 254 g["SerdeError"] = SerdeError 255 g["UserError"] = UserError 256 g["raise_unsupported_type"] = raise_unsupported_type 257 g["typename"] = typename 258 g["ensure"] = ensure 259 g["typing"] = typing 260 g["collections"] = collections 261 g["Literal"] = Literal 262 g["from_obj"] = from_obj 263 g["get_generic_arg"] = get_generic_arg 264 g["is_instance"] = is_instance 265 g["TypeCheck"] = TypeCheck 266 g["disabled"] = disabled 267 g["coerce_object"] = coerce_object 268 g["_exists_by_aliases"] = _exists_by_aliases 269 g["_get_by_aliases"] = _get_by_aliases 270 g["class_deserializers"] = class_deserializers 271 g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation 272 g["is_bearable"] = is_bearable 273 if deserializer: 274 g["serde_legacy_custom_class_deserializer"] = functools.partial( 275 serde_legacy_custom_class_deserializer, custom=deserializer 276 ) 277 278 # Collect types used in the generated code. 279 for typ in iter_types(cls): 280 # When we encounter a dataclass not marked with deserialize, then also generate 281 # deserialize functions for it. 282 if is_dataclass_without_de(typ) and typ is not cls: 283 # We call deserialize and not wrap to make sure that we will use the default serde 284 # configuration for generating the deserialization function. 285 deserialize(typ) 286 287 # We don't want to add primitive class e.g "str" into the scope, but primitive 288 # compatible types such as IntEnum and a subclass of primitives are added, 289 # so that generated code can use those types. 290 if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ): 291 continue 292 293 if is_generic(typ): 294 g[typename(typ)] = get_origin(typ) 295 else: 296 g[typename(typ)] = typ 297 298 # render all union functions 299 for union in iter_unions(cls): 300 union_args = type_args(union) 301 add_func( 302 scope, 303 union_func_name(UNION_DE_PREFIX, union_args), 304 render_union_func(cls, union_args, tagging), 305 g, 306 ) 307 308 # render literal functions 309 for literal in iter_literals(cls): 310 literal_args = type_args(literal) 311 add_func( 312 scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g 313 ) 314 315 # Collect default values and default factories used in the generated code. 316 for f in defields(cls): 317 assert f.name 318 if has_default(f): 319 scope.defaults[f.name] = f.default 320 elif has_default_factory(f): 321 scope.defaults[f.name] = f.default_factory 322 if f.deserializer: 323 g[f.deserializer.name] = f.deserializer 324 325 add_func( 326 scope, 327 FROM_ITER, 328 render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer), 329 g, 330 ) 331 add_func( 332 scope, 333 FROM_DICT, 334 render_from_dict( 335 cls, 336 rename_all, 337 deserializer, 338 type_check, 339 class_deserializer=class_deserializer, 340 deny_unknown_fields=deny_unknown_fields, 341 ), 342 g, 343 ) 344 345 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 346 347 stack.pop() 348 return cls 349 350 if _cls is None: 351 return wrap # type: ignore 352 353 if _cls in GENERATION_STACK: 354 return _cls 355 356 GENERATION_STACK.append(_cls) 357 try: 358 return wrap(_cls) 359 finally: 360 GENERATION_STACK.pop()
A dataclass with this decorator is deserializable from any of the data formats supported by pyserde.
>>> from serde import deserialize
>>> from serde.json import from_json
>>>
>>> @deserialize
... class Foo:
... i: int
... s: str
... f: float
... b: bool
>>>
>>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
Foo(i=10, s='foo', f=100.0, b=True)
304def is_serializable(instance_or_class: Any) -> bool: 305 """ 306 Test if an instance or class is serializable. 307 308 >>> @serialize 309 ... class Foo: 310 ... pass 311 312 Testing `Foo` class object returns `True`. 313 >>> is_serializable(Foo) 314 True 315 316 Testing `Foo` object also returns `True`. 317 >>> is_serializable(Foo()) 318 True 319 """ 320 return hasattr(instance_or_class, SERDE_SCOPE)
Test if an instance or class is serializable.
>>> @serialize
... class Foo:
... pass
Testing Foo
class object returns True
.
>>> is_serializable(Foo)
True
Testing Foo
object also returns True
.
>>> is_serializable(Foo())
True
363def is_deserializable(instance_or_class: Any) -> bool: 364 """ 365 Test if an instance or class is deserializable. 366 367 >>> @deserialize 368 ... class Foo: 369 ... pass 370 >>> 371 >>> is_deserializable(Foo) 372 True 373 """ 374 return hasattr(instance_or_class, SERDE_SCOPE)
Test if an instance or class is deserializable.
>>> @deserialize
... class Foo:
... pass
>>>
>>> is_deserializable(Foo)
True
451def to_dict( 452 o: Any, 453 c: Optional[type[Any]] = None, 454 reuse_instances: Optional[bool] = None, 455 convert_sets: Optional[bool] = None, 456 skip_none: bool = False, 457) -> dict[Any, Any]: 458 """ 459 Serialize object into python dictionary. This function ensures that the dataclass's fields are 460 accurately represented as key-value pairs in the resulting dictionary. 461 462 * `o`: Any pyserde object that you want to convert to `dict` 463 * `c`: Optional class argument 464 * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer 465 instead of converting them to serializable representation e.g. string. This behaviour allows 466 to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially 467 improve performance. 468 * `convert_sets`: This option controls how sets are handled during serialization and 469 deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during 470 serialization and back to sets during deserialization. This is useful for data formats that 471 do not natively support sets. 472 * `skip_none`: When set to True, any field in the class with a None value is excluded from the 473 serialized output. Defaults to False. 474 475 >>> from serde import serde 476 >>> @serde 477 ... class Foo: 478 ... i: int 479 ... s: str = 'foo' 480 ... f: float = 100.0 481 ... b: bool = True 482 >>> 483 >>> to_dict(Foo(i=10)) 484 {'i': 10, 's': 'foo', 'f': 100.0, 'b': True} 485 486 You can serialize not only pyserde objects but also objects of any supported types. For example, 487 the following example serializes list of pyserde objects into dict. 488 489 >>> lst = [Foo(i=10), Foo(i=20)] 490 >>> to_dict(lst) 491 [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 492 """ 493 return to_obj( # type: ignore 494 o, 495 named=True, 496 c=c, 497 reuse_instances=reuse_instances, 498 convert_sets=convert_sets, 499 skip_none=skip_none, 500 )
Serialize object into python dictionary. This function ensures that the dataclass's fields are accurately represented as key-value pairs in the resulting dictionary.
o
: Any pyserde object that you want to convert todict
c
: Optional class argumentreuse_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. Whenconvert_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}]
520def from_dict(cls: Any, o: dict[str, Any], reuse_instances: Optional[bool] = None) -> Any: 521 """ 522 Deserialize dictionary into object. 523 524 >>> @deserialize 525 ... class Foo: 526 ... i: int 527 ... s: str = 'foo' 528 ... f: float = 100.0 529 ... b: bool = True 530 >>> 531 >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}) 532 Foo(i=10, s='foo', f=100.0, b=True) 533 534 You can pass any type supported by pyserde. For example, 535 536 >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, 537 ... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 538 >>> from_dict(list[Foo], lst) 539 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 540 """ 541 return from_obj(cls, o, named=True, reuse_instances=reuse_instances)
Deserialize dictionary into object.
>>> @deserialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
Foo(i=10, s='foo', f=100.0, b=True)
You can pass any type supported by pyserde. For example,
>>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
>>> from_dict(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
408def to_tuple( 409 o: Any, 410 c: Optional[type[Any]] = None, 411 reuse_instances: Optional[bool] = None, 412 convert_sets: Optional[bool] = None, 413 skip_none: bool = False, 414) -> tuple[Any, ...]: 415 """ 416 Serialize object into tuple. 417 418 >>> @serialize 419 ... class Foo: 420 ... i: int 421 ... s: str = 'foo' 422 ... f: float = 100.0 423 ... b: bool = True 424 >>> 425 >>> to_tuple(Foo(i=10)) 426 (10, 'foo', 100.0, True) 427 428 You can pass any type supported by pyserde. For example, 429 430 >>> lst = [Foo(i=10), Foo(i=20)] 431 >>> to_tuple(lst) 432 [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 433 """ 434 return to_obj( # type: ignore 435 o, 436 named=False, 437 c=c, 438 reuse_instances=reuse_instances, 439 convert_sets=convert_sets, 440 skip_none=skip_none, 441 )
Serialize object into tuple.
>>> @serialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> to_tuple(Foo(i=10))
(10, 'foo', 100.0, True)
You can pass any type supported by pyserde. For example,
>>> lst = [Foo(i=10), Foo(i=20)]
>>> to_tuple(lst)
[(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
552def from_tuple(cls: Any, o: Any, reuse_instances: Optional[bool] = None) -> Any: 553 """ 554 Deserialize tuple into object. 555 556 >>> @deserialize 557 ... class Foo: 558 ... i: int 559 ... s: str = 'foo' 560 ... f: float = 100.0 561 ... b: bool = True 562 >>> 563 >>> from_tuple(Foo, (10, 'foo', 100.0, True)) 564 Foo(i=10, s='foo', f=100.0, b=True) 565 566 You can pass any type supported by pyserde. For example, 567 568 >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 569 >>> from_tuple(list[Foo], lst) 570 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 571 """ 572 return from_obj(cls, o, named=False, reuse_instances=reuse_instances)
Deserialize tuple into object.
>>> @deserialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> from_tuple(Foo, (10, 'foo', 100.0, True))
Foo(i=10, s='foo', f=100.0, b=True)
You can pass any type supported by pyserde. For example,
>>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
>>> from_tuple(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
Serde error class.
Skip a field in custom (de)serializer.
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.
126def default_deserializer(_cls: type[Any], obj: Any) -> Any: 127 """ 128 Marker function to tell serde to use the default deserializer. It's used when custom 129 deserializer is specified at the class but you want to override a field with the default 130 deserializer. 131 """
Marker function to tell serde to use the default deserializer. It's used when custom deserializer is specified at the class but you want to override a field with the default deserializer.
444def asdict(v: Any) -> dict[Any, Any]: 445 """ 446 Serialize object into dictionary. 447 """ 448 return to_dict(v, reuse_instances=False, convert_sets=False)
Serialize object into dictionary.
401def astuple(v: Any) -> tuple[Any, ...]: 402 """ 403 Serialize object into tuple. 404 """ 405 return to_tuple(v, reuse_instances=False, convert_sets=False)
Serialize object into tuple.
96def default_serializer(_cls: type[Any], obj: Any) -> Any: 97 """ 98 Marker function to tell serde to use the default serializer. It's used when custom serializer 99 is specified at the class but you want to override a field with the default serializer. 100 """
Marker function to tell serde to use the default serializer. It's used when custom serializer is specified at the class but you want to override a field with the default serializer.
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")
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)
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")
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)
1043def add_serializer(serializer: ClassSerializer) -> None: 1044 """ 1045 Register custom global serializer. 1046 """ 1047 GLOBAL_CLASS_SERIALIZER.append(serializer)
Register custom global serializer.
1050def add_deserializer(deserializer: ClassDeserializer) -> None: 1051 """ 1052 Register custom global deserializer. 1053 """ 1054 GLOBAL_CLASS_DESERIALIZER.append(deserializer)
Register custom global deserializer.