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,collections.abc.Sequence,collections.abc.MutableSequence,tupleset,collections.abc.Set,collections.abc.MutableSetdict,collections.abc.Mapping,collections.abc.MutableMappingfrozenset,defaultdict,deque,Counter
typing.Optionaltyping.Union- User defined class with
@dataclass typing.NewTypefor primitive typestyping.Anytyping.Literaltyping.Generictyping.ClassVardataclasses.InitVarEnumandIntEnum- Standard library
- PyPI library
numpytypesSQLAlchemyDeclarative 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.seandserde.demodules.serde.compat: Compatibility layer which handles mostly differences oftypingmodule 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 Any, Type, overload 27 28from typing_extensions import dataclass_transform 29 30from .compat import SerdeError, SerdeSkip, T 31from .core import ( 32 ClassSerializer, 33 ClassDeserializer, 34 AdjacentTagging, 35 coerce, 36 DefaultTagging, 37 ExternalTagging, 38 InternalTagging, 39 disabled, 40 strict, 41 Tagging, 42 TypeCheck, 43 Untagged, 44 field, 45 init, 46 logger, 47 should_impl_dataclass, 48 add_serializer, 49 add_deserializer, 50) 51from .de import ( 52 DeserializeFunc, 53 default_deserializer, 54 deserialize, 55 from_dict, 56 from_tuple, 57 is_deserializable, 58) 59from .se import ( 60 SerializeFunc, 61 asdict, 62 astuple, 63 default_serializer, 64 is_serializable, 65 serialize, 66 to_dict, 67 to_tuple, 68) 69 70__all__ = [ 71 "serde", 72 "serialize", 73 "deserialize", 74 "is_serializable", 75 "is_deserializable", 76 "to_dict", 77 "from_dict", 78 "to_tuple", 79 "from_tuple", 80 "SerdeError", 81 "SerdeSkip", 82 "AdjacentTagging", 83 "ExternalTagging", 84 "InternalTagging", 85 "Untagged", 86 "disabled", 87 "strict", 88 "coerce", 89 "field", 90 "default_deserializer", 91 "asdict", 92 "astuple", 93 "default_serializer", 94 "compat", 95 "core", 96 "de", 97 "inspect", 98 "json", 99 "msgpack", 100 "numpy", 101 "se", 102 "toml", 103 "pickle", 104 "yaml", 105 "init", 106 "logger", 107 "ClassSerializer", 108 "ClassDeserializer", 109 "add_serializer", 110 "add_deserializer", 111] 112 113 114@overload 115def serde( 116 _cls: Type[T], 117 rename_all: str | None = None, 118 reuse_instances_default: bool = True, 119 convert_sets_default: bool = False, 120 transparent: bool = False, 121 serializer: SerializeFunc | None = None, 122 deserializer: DeserializeFunc | None = None, 123 tagging: Tagging = DefaultTagging, 124 type_check: TypeCheck = strict, 125 serialize_class_var: bool = False, 126 class_serializer: ClassSerializer | None = None, 127 class_deserializer: ClassDeserializer | None = None, 128 deny_unknown_fields: bool = False, 129) -> Type[T]: ... 130 131 132@overload 133def serde( 134 _cls: Any = None, 135 rename_all: str | None = None, 136 reuse_instances_default: bool = True, 137 convert_sets_default: bool = False, 138 transparent: bool = False, 139 serializer: SerializeFunc | None = None, 140 deserializer: DeserializeFunc | None = None, 141 tagging: Tagging = DefaultTagging, 142 type_check: TypeCheck = strict, 143 serialize_class_var: bool = False, 144 class_serializer: ClassSerializer | None = None, 145 class_deserializer: ClassDeserializer | None = None, 146 deny_unknown_fields: bool = False, 147) -> Callable[[type[T]], type[T]]: ... 148 149 150@dataclass_transform(field_specifiers=(field,)) 151def serde( 152 _cls: Any = None, 153 rename_all: str | None = None, 154 reuse_instances_default: bool = True, 155 convert_sets_default: bool = False, 156 transparent: bool = False, 157 serializer: SerializeFunc | None = None, 158 deserializer: DeserializeFunc | None = None, 159 tagging: Tagging = DefaultTagging, 160 type_check: TypeCheck = strict, 161 serialize_class_var: bool = False, 162 class_serializer: ClassSerializer | None = None, 163 class_deserializer: ClassDeserializer | None = None, 164 deny_unknown_fields: bool = False, 165) -> Any: 166 """ 167 serde decorator. Keyword arguments are passed in `serialize` and `deserialize`. 168 """ 169 170 def wrap(cls: Any) -> Any: 171 if should_impl_dataclass(cls): 172 dataclass(cls) 173 serialize( 174 cls, 175 rename_all=rename_all, 176 reuse_instances_default=reuse_instances_default, 177 convert_sets_default=convert_sets_default, 178 transparent=transparent, 179 serializer=serializer, 180 deserializer=deserializer, 181 tagging=tagging, 182 type_check=type_check, 183 serialize_class_var=serialize_class_var, 184 class_serializer=class_serializer, 185 ) 186 deserialize( 187 cls, 188 rename_all=rename_all, 189 reuse_instances_default=reuse_instances_default, 190 convert_sets_default=convert_sets_default, 191 transparent=transparent, 192 serializer=serializer, 193 deserializer=deserializer, 194 tagging=tagging, 195 type_check=type_check, 196 serialize_class_var=serialize_class_var, 197 class_deserializer=class_deserializer, 198 deny_unknown_fields=deny_unknown_fields, 199 ) 200 return cls 201 202 if _cls is None: 203 return wrap 204 205 return wrap(_cls)
151@dataclass_transform(field_specifiers=(field,)) 152def serde( 153 _cls: Any = None, 154 rename_all: str | None = None, 155 reuse_instances_default: bool = True, 156 convert_sets_default: bool = False, 157 transparent: bool = False, 158 serializer: SerializeFunc | None = None, 159 deserializer: DeserializeFunc | None = None, 160 tagging: Tagging = DefaultTagging, 161 type_check: TypeCheck = strict, 162 serialize_class_var: bool = False, 163 class_serializer: ClassSerializer | None = None, 164 class_deserializer: ClassDeserializer | None = None, 165 deny_unknown_fields: bool = False, 166) -> Any: 167 """ 168 serde decorator. Keyword arguments are passed in `serialize` and `deserialize`. 169 """ 170 171 def wrap(cls: Any) -> Any: 172 if should_impl_dataclass(cls): 173 dataclass(cls) 174 serialize( 175 cls, 176 rename_all=rename_all, 177 reuse_instances_default=reuse_instances_default, 178 convert_sets_default=convert_sets_default, 179 transparent=transparent, 180 serializer=serializer, 181 deserializer=deserializer, 182 tagging=tagging, 183 type_check=type_check, 184 serialize_class_var=serialize_class_var, 185 class_serializer=class_serializer, 186 ) 187 deserialize( 188 cls, 189 rename_all=rename_all, 190 reuse_instances_default=reuse_instances_default, 191 convert_sets_default=convert_sets_default, 192 transparent=transparent, 193 serializer=serializer, 194 deserializer=deserializer, 195 tagging=tagging, 196 type_check=type_check, 197 serialize_class_var=serialize_class_var, 198 class_deserializer=class_deserializer, 199 deny_unknown_fields=deny_unknown_fields, 200 ) 201 return cls 202 203 if _cls is None: 204 return wrap 205 206 return wrap(_cls)
serde decorator. Keyword arguments are passed in serialize and deserialize.
196@dataclass_transform() 197def serialize( 198 _cls: type[T] | None = None, 199 rename_all: str | None = None, 200 reuse_instances_default: bool = False, 201 convert_sets_default: bool = False, 202 serializer: SerializeFunc | None = None, 203 tagging: Tagging = DefaultTagging, 204 type_check: TypeCheck = strict, 205 serialize_class_var: bool = False, 206 transparent: bool = False, 207 class_serializer: ClassSerializer | None = None, 208 **kwargs: Any, 209) -> type[T]: 210 """ 211 A dataclass with this decorator is serializable into any of the data formats 212 supported by pyserde. 213 214 >>> from datetime import datetime 215 >>> from serde import serialize 216 >>> from serde.json import to_json 217 >>> 218 >>> @serialize 219 ... class Foo: 220 ... i: int 221 ... s: str 222 ... f: float 223 ... b: bool 224 >>> 225 >>> to_json(Foo(i=10, s='foo', f=100.0, b=True)) 226 '{"i":10,"s":"foo","f":100.0,"b":true}' 227 """ 228 229 def wrap(cls: type[T]) -> type[T]: 230 tagging.check() 231 232 # If no `dataclass` found in the class, dataclassify it automatically. 233 if not is_dataclass(cls): 234 dataclass(cls) 235 236 if transparent: 237 get_transparent_field(cls) 238 239 if type_check.is_strict(): 240 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 241 serde_beartype(cls) 242 243 g: dict[str, Any] = {} 244 245 # Create a scope storage used by serde. 246 # Each class should get own scope. Child classes can not share scope with parent class. 247 # That's why we need the "scope.cls is not cls" check. 248 scope: Scope | None = getattr(cls, SERDE_SCOPE, None) 249 if scope is None or scope.cls is not cls: 250 scope = Scope( 251 cls, 252 reuse_instances_default=reuse_instances_default, 253 convert_sets_default=convert_sets_default, 254 ) 255 setattr(cls, SERDE_SCOPE, scope) 256 scope.transparent = transparent 257 258 class_serializers: list[ClassSerializer] = list( 259 itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else []) 260 ) 261 262 # Set some globals for all generated functions 263 g["cls"] = cls 264 g["copy"] = copy 265 g["serde_scope"] = scope 266 g["SerdeError"] = SerdeError 267 g["enum_value"] = enum_value 268 g["is_dataclass"] = is_dataclass 269 g["typename"] = typename # used in union functions 270 g["is_instance"] = is_instance # used in union functions 271 g["to_obj"] = to_obj 272 g["typing"] = typing 273 g["Literal"] = Literal 274 g["TypeCheck"] = TypeCheck 275 g["disabled"] = disabled 276 g["coerce_object"] = coerce_object 277 g["class_serializers"] = class_serializers 278 if serializer: 279 g["serde_legacy_custom_class_serializer"] = functools.partial( 280 serde_legacy_custom_class_serializer, custom=serializer 281 ) 282 283 # Collect types used in the generated code. 284 for typ in iter_types(cls): 285 # When we encounter a dataclass not marked with serialize, then also generate serialize 286 # functions for it. 287 if is_dataclass_without_se(typ) and typ is not cls: 288 # We call serialize and not wrap to make sure that we will use the default serde 289 # configuration for generating the serialization function. 290 serialize(typ) 291 292 if is_primitive(typ) and not is_enum(typ): 293 continue 294 g[typename(typ)] = typ 295 296 # render all union functions 297 for union in iter_unions(cls): 298 union_args = list(type_args(union)) 299 union_key = union_func_name(UNION_SE_PREFIX, union_args) 300 add_func(scope, union_key, render_union_func(cls, union_args, tagging), g) 301 scope.union_se_args[union_key] = union_args 302 303 for f in sefields(cls, serialize_class_var): 304 if f.skip_if: 305 g[f.skip_if.name] = f.skip_if 306 if f.serializer: 307 g[f.serializer.name] = f.serializer 308 309 add_func( 310 scope, 311 TO_ITER, 312 render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer), 313 g, 314 ) 315 add_func( 316 scope, 317 TO_DICT, 318 render_to_dict( 319 cls, rename_all, serializer, type_check, serialize_class_var, class_serializer 320 ), 321 g, 322 ) 323 324 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 325 326 return cls 327 328 if _cls is None: 329 return wrap # type: ignore 330 331 if _cls in GENERATION_STACK: 332 return _cls 333 334 GENERATION_STACK.append(_cls) 335 try: 336 return wrap(_cls) 337 finally: 338 GENERATION_STACK.pop()
A dataclass with this decorator is serializable into any of the data formats supported by pyserde.
>>> from datetime import datetime
>>> from serde import serialize
>>> from serde.json import to_json
>>>
>>> @serialize
... class Foo:
... i: int
... s: str
... f: float
... b: bool
>>>
>>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
'{"i":10,"s":"foo","f":100.0,"b":true}'
205@dataclass_transform() 206def deserialize( 207 _cls: type[T] | None = None, 208 rename_all: str | None = None, 209 reuse_instances_default: bool = True, 210 convert_sets_default: bool = False, 211 deserializer: DeserializeFunc | None = None, 212 tagging: Tagging = DefaultTagging, 213 type_check: TypeCheck = strict, 214 class_deserializer: ClassDeserializer | None = None, 215 deny_unknown_fields: bool = False, 216 transparent: bool = False, 217 **kwargs: Any, 218) -> type[T]: 219 """ 220 A dataclass with this decorator is deserializable from any of the data formats supported 221 by pyserde. 222 223 >>> from serde import deserialize 224 >>> from serde.json import from_json 225 >>> 226 >>> @deserialize 227 ... class Foo: 228 ... i: int 229 ... s: str 230 ... f: float 231 ... b: bool 232 >>> 233 >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}') 234 Foo(i=10, s='foo', f=100.0, b=True) 235 """ 236 237 stack = [] 238 239 def wrap(cls: type[T]) -> type[T]: 240 if cls in stack: 241 return cls 242 stack.append(cls) 243 244 tagging.check() 245 246 # If no `dataclass` found in the class, dataclassify it automatically. 247 if not is_dataclass(cls): 248 dataclass(cls) 249 250 if transparent: 251 get_transparent_field(cls) 252 253 if type_check.is_strict(): 254 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 255 serde_beartype(cls) 256 257 g: dict[str, Any] = {} 258 259 # Create a scope storage used by serde. 260 # Each class should get own scope. Child classes can not share scope with parent class. 261 # That's why we need the "scope.cls is not cls" check. 262 scope: Scope | None = getattr(cls, SERDE_SCOPE, None) 263 if scope is None or scope.cls is not cls: 264 scope = Scope( 265 cls, 266 reuse_instances_default=reuse_instances_default, 267 convert_sets_default=convert_sets_default, 268 ) 269 setattr(cls, SERDE_SCOPE, scope) 270 scope.transparent = transparent 271 272 class_deserializers: list[ClassDeserializer] = list( 273 itertools.chain( 274 GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else [] 275 ) 276 ) 277 278 # Set some globals for all generated functions 279 g["cls"] = cls 280 g["serde_scope"] = scope 281 g["SerdeError"] = SerdeError 282 g["UserError"] = UserError 283 g["typename"] = typename 284 g["ensure"] = ensure 285 g["typing"] = typing 286 g["collections"] = collections 287 g["Literal"] = Literal 288 g["from_obj"] = from_obj 289 g["get_generic_arg"] = get_generic_arg 290 g["is_instance"] = is_instance 291 g["TypeCheck"] = TypeCheck 292 g["disabled"] = disabled 293 g["coerce_object"] = coerce_object 294 g["_exists_by_aliases"] = _exists_by_aliases 295 g["_get_by_aliases"] = _get_by_aliases 296 g["class_deserializers"] = class_deserializers 297 g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation 298 g["is_bearable"] = is_bearable 299 if deserializer: 300 g["serde_legacy_custom_class_deserializer"] = functools.partial( 301 serde_legacy_custom_class_deserializer, custom=deserializer 302 ) 303 304 # Collect types used in the generated code. 305 for typ in iter_types(cls): 306 # When we encounter a dataclass not marked with deserialize, then also generate 307 # deserialize functions for it. 308 if is_dataclass_without_de(typ) and typ is not cls: 309 # We call deserialize and not wrap to make sure that we will use the default serde 310 # configuration for generating the deserialization function. 311 deserialize(typ) 312 313 # We don't want to add primitive class e.g "str" into the scope, but primitive 314 # compatible types such as IntEnum and a subclass of primitives are added, 315 # so that generated code can use those types. 316 if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ): 317 continue 318 319 if is_generic(typ): 320 g[typename(typ)] = get_origin(typ) 321 else: 322 g[typename(typ)] = typ 323 324 # render all union functions 325 for union in iter_unions(cls): 326 union_args = type_args(union) 327 add_func( 328 scope, 329 union_func_name(UNION_DE_PREFIX, union_args), 330 render_union_func(cls, union_args, tagging), 331 g, 332 ) 333 334 # render literal functions 335 for literal in iter_literals(cls): 336 literal_args = type_args(literal) 337 add_func( 338 scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g 339 ) 340 341 # Collect default values and default factories used in the generated code. 342 for f in defields(cls): 343 assert f.name 344 if has_default(f): 345 scope.defaults[f.name] = f.default 346 elif has_default_factory(f): 347 scope.defaults[f.name] = f.default_factory 348 if f.deserializer: 349 g[f.deserializer.name] = f.deserializer 350 351 add_func( 352 scope, 353 FROM_ITER, 354 render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer), 355 g, 356 ) 357 add_func( 358 scope, 359 FROM_DICT, 360 render_from_dict( 361 cls, 362 rename_all, 363 deserializer, 364 type_check, 365 class_deserializer=class_deserializer, 366 deny_unknown_fields=deny_unknown_fields, 367 ), 368 g, 369 ) 370 371 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 372 373 stack.pop() 374 return cls 375 376 if _cls is None: 377 return wrap # type: ignore 378 379 if _cls in GENERATION_STACK: 380 return _cls 381 382 GENERATION_STACK.append(_cls) 383 try: 384 return wrap(_cls) 385 finally: 386 GENERATION_STACK.pop()
A dataclass with this decorator is deserializable from any of the data formats supported by pyserde.
>>> from serde import deserialize
>>> from serde.json import from_json
>>>
>>> @deserialize
... class Foo:
... i: int
... s: str
... f: float
... b: bool
>>>
>>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
Foo(i=10, s='foo', f=100.0, b=True)
341def is_serializable(instance_or_class: Any) -> bool: 342 """ 343 Test if an instance or class is serializable. 344 345 >>> @serialize 346 ... class Foo: 347 ... pass 348 349 Testing `Foo` class object returns `True`. 350 >>> is_serializable(Foo) 351 True 352 353 Testing `Foo` object also returns `True`. 354 >>> is_serializable(Foo()) 355 True 356 """ 357 return hasattr(instance_or_class, SERDE_SCOPE)
Test if an instance or class is serializable.
>>> @serialize
... class Foo:
... pass
Testing Foo class object returns True.
>>> is_serializable(Foo)
True
Testing Foo object also returns True.
>>> is_serializable(Foo())
True
389def is_deserializable(instance_or_class: Any) -> bool: 390 """ 391 Test if an instance or class is deserializable. 392 393 >>> @deserialize 394 ... class Foo: 395 ... pass 396 >>> 397 >>> is_deserializable(Foo) 398 True 399 """ 400 return hasattr(instance_or_class, SERDE_SCOPE)
Test if an instance or class is deserializable.
>>> @deserialize
... class Foo:
... pass
>>>
>>> is_deserializable(Foo)
True
492def to_dict( 493 o: Any, 494 c: type[Any] | None = None, 495 reuse_instances: bool | None = None, 496 convert_sets: bool | None = None, 497 skip_none: bool = False, 498) -> dict[Any, Any]: 499 """ 500 Serialize object into python dictionary. This function ensures that the dataclass's fields are 501 accurately represented as key-value pairs in the resulting dictionary. 502 503 * `o`: Any pyserde object that you want to convert to `dict` 504 * `c`: Optional class argument 505 * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer 506 instead of converting them to serializable representation e.g. string. This behaviour allows 507 to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially 508 improve performance. 509 * `convert_sets`: This option controls how sets are handled during serialization and 510 deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during 511 serialization and back to sets during deserialization. This is useful for data formats that 512 do not natively support sets. 513 * `skip_none`: When set to True, any field in the class with a None value is excluded from the 514 serialized output. Defaults to False. 515 516 >>> from serde import serde 517 >>> @serde 518 ... class Foo: 519 ... i: int 520 ... s: str = 'foo' 521 ... f: float = 100.0 522 ... b: bool = True 523 >>> 524 >>> to_dict(Foo(i=10)) 525 {'i': 10, 's': 'foo', 'f': 100.0, 'b': True} 526 527 You can serialize not only pyserde objects but also objects of any supported types. For example, 528 the following example serializes list of pyserde objects into dict. 529 530 >>> lst = [Foo(i=10), Foo(i=20)] 531 >>> to_dict(lst) 532 [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 533 """ 534 return to_obj( # type: ignore 535 o, 536 named=True, 537 c=c, 538 reuse_instances=reuse_instances, 539 convert_sets=convert_sets, 540 skip_none=skip_none, 541 )
Serialize object into python dictionary. This function ensures that the dataclass's fields are accurately represented as key-value pairs in the resulting dictionary.
o: Any pyserde object that you want to convert todictc: 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.pyyamland potentially improve performance.convert_sets: This option controls how sets are handled during serialization and deserialization. Whenconvert_setsis 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}]
589def from_dict( 590 cls: Any, 591 o: dict[str, Any], 592 reuse_instances: bool | None = None, 593 deserialize_numbers: Callable[[str | int], float] | None = None, 594) -> Any: 595 """ 596 Deserialize dictionary into object. 597 598 >>> @deserialize 599 ... class Foo: 600 ... i: int 601 ... s: str = 'foo' 602 ... f: float = 100.0 603 ... b: bool = True 604 >>> 605 >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}) 606 Foo(i=10, s='foo', f=100.0, b=True) 607 608 You can pass any type supported by pyserde. For example, 609 610 * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target 611 type is float (e.g. accept ints or numeric strings supplied by a parser). 612 613 >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, 614 ... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 615 >>> from_dict(list[Foo], lst) 616 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 617 """ 618 return from_obj( 619 cls, 620 o, 621 named=True, 622 reuse_instances=reuse_instances, 623 deserialize_numbers=deserialize_numbers, 624 )
Deserialize dictionary into object.
>>> @deserialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
Foo(i=10, s='foo', f=100.0, b=True)
You can pass any type supported by pyserde. For example,
deserialize_numbers: Optional callable to coerce numeric input to floats when the target type is float (e.g. accept ints or numeric strings supplied by a parser).
>>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
>>> from_dict(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
449def to_tuple( 450 o: Any, 451 c: type[Any] | None = None, 452 reuse_instances: bool | None = None, 453 convert_sets: bool | None = None, 454 skip_none: bool = False, 455) -> tuple[Any, ...]: 456 """ 457 Serialize object into tuple. 458 459 >>> @serialize 460 ... class Foo: 461 ... i: int 462 ... s: str = 'foo' 463 ... f: float = 100.0 464 ... b: bool = True 465 >>> 466 >>> to_tuple(Foo(i=10)) 467 (10, 'foo', 100.0, True) 468 469 You can pass any type supported by pyserde. For example, 470 471 >>> lst = [Foo(i=10), Foo(i=20)] 472 >>> to_tuple(lst) 473 [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 474 """ 475 return to_obj( # type: ignore 476 o, 477 named=False, 478 c=c, 479 reuse_instances=reuse_instances, 480 convert_sets=convert_sets, 481 skip_none=skip_none, 482 )
Serialize object into tuple.
>>> @serialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> to_tuple(Foo(i=10))
(10, 'foo', 100.0, True)
You can pass any type supported by pyserde. For example,
>>> lst = [Foo(i=10), Foo(i=20)]
>>> to_tuple(lst)
[(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
645def from_tuple( 646 cls: Any, 647 o: Any, 648 reuse_instances: bool | None = None, 649 deserialize_numbers: Callable[[str | int], float] | None = None, 650) -> Any: 651 """ 652 Deserialize tuple into object. 653 654 >>> @deserialize 655 ... class Foo: 656 ... i: int 657 ... s: str = 'foo' 658 ... f: float = 100.0 659 ... b: bool = True 660 >>> 661 >>> from_tuple(Foo, (10, 'foo', 100.0, True)) 662 Foo(i=10, s='foo', f=100.0, b=True) 663 664 You can pass any type supported by pyserde. For example, 665 666 * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target 667 type is float (e.g. accept ints or numeric strings supplied by a parser). 668 669 >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 670 >>> from_tuple(list[Foo], lst) 671 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 672 """ 673 return from_obj( 674 cls, 675 o, 676 named=False, 677 reuse_instances=reuse_instances, 678 deserialize_numbers=deserialize_numbers, 679 )
Deserialize tuple into object.
>>> @deserialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> from_tuple(Foo, (10, 'foo', 100.0, True))
Foo(i=10, s='foo', f=100.0, b=True)
You can pass any type supported by pyserde. For example,
deserialize_numbers: Optional callable to coerce numeric input to floats when the target type is float (e.g. accept ints or numeric strings supplied by a parser).
>>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
>>> from_tuple(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
Serde error class.
Skip a field in custom (de)serializer.
567def field( 568 *args: Any, 569 rename: str | None = None, 570 alias: list[str] | None = None, 571 skip: bool | None = None, 572 skip_if: Callable[[Any], Any] | None = None, 573 skip_if_false: bool | None = None, 574 skip_if_default: bool | None = None, 575 serializer: Callable[..., Any] | None = None, 576 deserializer: Callable[..., Any] | None = None, 577 flatten: FlattenOpts | bool | None = None, 578 metadata: dict[str, Any] | None = None, 579 **kwargs: Any, 580) -> Any: 581 """ 582 Declare a field with parameters. 583 """ 584 if not metadata: 585 metadata = {} 586 587 if rename is not None: 588 metadata["serde_rename"] = rename 589 if alias is not None: 590 metadata["serde_alias"] = alias 591 if skip is not None: 592 metadata["serde_skip"] = skip 593 if skip_if is not None: 594 metadata["serde_skip_if"] = skip_if 595 if skip_if_false is not None: 596 metadata["serde_skip_if_false"] = skip_if_false 597 if skip_if_default is not None: 598 metadata["serde_skip_if_default"] = skip_if_default 599 if serializer: 600 metadata["serde_serializer"] = serializer 601 if deserializer: 602 metadata["serde_deserializer"] = deserializer 603 if flatten is True: 604 metadata["serde_flatten"] = FlattenOpts() 605 elif flatten: 606 metadata["serde_flatten"] = flatten 607 608 return dataclasses.field(*args, metadata=metadata, **kwargs)
Declare a field with parameters.
144def default_deserializer(_cls: type[Any], obj: Any) -> Any: 145 """ 146 Marker function to tell serde to use the default deserializer. It's used when custom 147 deserializer is specified at the class but you want to override a field with the default 148 deserializer. 149 """
Marker function to tell serde to use the default deserializer. It's used when custom deserializer is specified at the class but you want to override a field with the default deserializer.
485def asdict(v: Any) -> dict[Any, Any]: 486 """ 487 Serialize object into dictionary. 488 """ 489 return to_dict(v, reuse_instances=False, convert_sets=False)
Serialize object into dictionary.
442def astuple(v: Any) -> tuple[Any, ...]: 443 """ 444 Serialize object into tuple. 445 """ 446 return to_tuple(v, reuse_instances=False, convert_sets=False)
Serialize object into tuple.
127def default_serializer(_cls: type[Any], obj: Any) -> Any: 128 """ 129 Marker function to tell serde to use the default serializer. It's used when custom serializer 130 is specified at the class but you want to override a field with the default serializer. 131 """
Marker function to tell serde to use the default serializer. It's used when custom serializer is specified at the class but you want to override a field with the default serializer.
1095class ClassSerializer(Protocol): 1096 """ 1097 Interface for custom class serializer. 1098 1099 This protocol is intended to be used for custom class serializer. 1100 1101 >>> from datetime import datetime 1102 >>> from serde import serde 1103 >>> from plum import dispatch 1104 >>> class MySerializer(ClassSerializer): 1105 ... @dispatch 1106 ... def serialize(self, value: datetime) -> str: 1107 ... return value.strftime("%d/%m/%y") 1108 """ 1109 1110 def serialize(self, value: Any) -> Any: 1111 pass
Interface for custom class serializer.
This protocol is intended to be used for custom class serializer.
>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MySerializer(ClassSerializer):
... @dispatch
... def serialize(self, value: datetime) -> str:
... return value.strftime("%d/%m/%y")
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)
1114class ClassDeserializer(Protocol): 1115 """ 1116 Interface for custom class deserializer. 1117 1118 This protocol is intended to be used for custom class deserializer. 1119 1120 >>> from datetime import datetime 1121 >>> from serde import serde 1122 >>> from plum import dispatch 1123 >>> class MyDeserializer(ClassDeserializer): 1124 ... @dispatch 1125 ... def deserialize(self, cls: type[datetime], value: Any) -> datetime: 1126 ... return datetime.strptime(value, "%d/%m/%y") 1127 """ 1128 1129 def deserialize(self, cls: Any, value: Any) -> Any: 1130 pass
Interface for custom class deserializer.
This protocol is intended to be used for custom class deserializer.
>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MyDeserializer(ClassDeserializer):
... @dispatch
... def deserialize(self, cls: type[datetime], value: Any) -> datetime:
... return datetime.strptime(value, "%d/%m/%y")
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)
1138def add_serializer(serializer: ClassSerializer) -> None: 1139 """ 1140 Register custom global serializer. 1141 """ 1142 GLOBAL_CLASS_SERIALIZER.append(serializer)
Register custom global serializer.
1145def add_deserializer(deserializer: ClassDeserializer) -> None: 1146 """ 1147 Register custom global deserializer. 1148 """ 1149 GLOBAL_CLASS_DESERIALIZER.append(deserializer)
Register custom global deserializer.