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:
Made with contrib.rocks.
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
.
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}'
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)
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
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
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 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}]
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)]
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)]
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)]
Serde error class.
Skip a field in custom (de)serializer.
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.
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.
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.
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.
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.
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")
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)
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")
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)
1044def add_serializer(serializer: ClassSerializer) -> None: 1045 """ 1046 Register custom global serializer. 1047 """ 1048 GLOBAL_CLASS_SERIALIZER.append(serializer)
Register custom global serializer.
1051def add_deserializer(deserializer: ClassDeserializer) -> None: 1052 """ 1053 Register custom global deserializer. 1054 """ 1055 GLOBAL_CLASS_DESERIALIZER.append(deserializer)
Register custom global deserializer.