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["raise_unsupported_type"] = raise_unsupported_type 268 g["enum_value"] = enum_value 269 g["is_dataclass"] = is_dataclass 270 g["typename"] = typename # used in union functions 271 g["is_instance"] = is_instance # used in union functions 272 g["to_obj"] = to_obj 273 g["typing"] = typing 274 g["Literal"] = Literal 275 g["TypeCheck"] = TypeCheck 276 g["disabled"] = disabled 277 g["coerce_object"] = coerce_object 278 g["class_serializers"] = class_serializers 279 if serializer: 280 g["serde_legacy_custom_class_serializer"] = functools.partial( 281 serde_legacy_custom_class_serializer, custom=serializer 282 ) 283 284 # Collect types used in the generated code. 285 for typ in iter_types(cls): 286 # When we encounter a dataclass not marked with serialize, then also generate serialize 287 # functions for it. 288 if is_dataclass_without_se(typ) and typ is not cls: 289 # We call serialize and not wrap to make sure that we will use the default serde 290 # configuration for generating the serialization function. 291 serialize(typ) 292 293 if is_primitive(typ) and not is_enum(typ): 294 continue 295 g[typename(typ)] = typ 296 297 # render all union functions 298 for union in iter_unions(cls): 299 union_args = list(type_args(union)) 300 union_key = union_func_name(UNION_SE_PREFIX, union_args) 301 add_func(scope, union_key, render_union_func(cls, union_args, tagging), g) 302 scope.union_se_args[union_key] = union_args 303 304 for f in sefields(cls, serialize_class_var): 305 if f.skip_if: 306 g[f.skip_if.name] = f.skip_if 307 if f.serializer: 308 g[f.serializer.name] = f.serializer 309 310 add_func( 311 scope, 312 TO_ITER, 313 render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer), 314 g, 315 ) 316 add_func( 317 scope, 318 TO_DICT, 319 render_to_dict( 320 cls, rename_all, serializer, type_check, serialize_class_var, class_serializer 321 ), 322 g, 323 ) 324 325 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 326 327 return cls 328 329 if _cls is None: 330 return wrap # type: ignore 331 332 if _cls in GENERATION_STACK: 333 return _cls 334 335 GENERATION_STACK.append(_cls) 336 try: 337 return wrap(_cls) 338 finally: 339 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["raise_unsupported_type"] = raise_unsupported_type 284 g["typename"] = typename 285 g["ensure"] = ensure 286 g["typing"] = typing 287 g["collections"] = collections 288 g["Literal"] = Literal 289 g["from_obj"] = from_obj 290 g["get_generic_arg"] = get_generic_arg 291 g["is_instance"] = is_instance 292 g["TypeCheck"] = TypeCheck 293 g["disabled"] = disabled 294 g["coerce_object"] = coerce_object 295 g["_exists_by_aliases"] = _exists_by_aliases 296 g["_get_by_aliases"] = _get_by_aliases 297 g["class_deserializers"] = class_deserializers 298 g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation 299 g["is_bearable"] = is_bearable 300 if deserializer: 301 g["serde_legacy_custom_class_deserializer"] = functools.partial( 302 serde_legacy_custom_class_deserializer, custom=deserializer 303 ) 304 305 # Collect types used in the generated code. 306 for typ in iter_types(cls): 307 # When we encounter a dataclass not marked with deserialize, then also generate 308 # deserialize functions for it. 309 if is_dataclass_without_de(typ) and typ is not cls: 310 # We call deserialize and not wrap to make sure that we will use the default serde 311 # configuration for generating the deserialization function. 312 deserialize(typ) 313 314 # We don't want to add primitive class e.g "str" into the scope, but primitive 315 # compatible types such as IntEnum and a subclass of primitives are added, 316 # so that generated code can use those types. 317 if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ): 318 continue 319 320 if is_generic(typ): 321 g[typename(typ)] = get_origin(typ) 322 else: 323 g[typename(typ)] = typ 324 325 # render all union functions 326 for union in iter_unions(cls): 327 union_args = type_args(union) 328 add_func( 329 scope, 330 union_func_name(UNION_DE_PREFIX, union_args), 331 render_union_func(cls, union_args, tagging), 332 g, 333 ) 334 335 # render literal functions 336 for literal in iter_literals(cls): 337 literal_args = type_args(literal) 338 add_func( 339 scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g 340 ) 341 342 # Collect default values and default factories used in the generated code. 343 for f in defields(cls): 344 assert f.name 345 if has_default(f): 346 scope.defaults[f.name] = f.default 347 elif has_default_factory(f): 348 scope.defaults[f.name] = f.default_factory 349 if f.deserializer: 350 g[f.deserializer.name] = f.deserializer 351 352 add_func( 353 scope, 354 FROM_ITER, 355 render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer), 356 g, 357 ) 358 add_func( 359 scope, 360 FROM_DICT, 361 render_from_dict( 362 cls, 363 rename_all, 364 deserializer, 365 type_check, 366 class_deserializer=class_deserializer, 367 deny_unknown_fields=deny_unknown_fields, 368 ), 369 g, 370 ) 371 372 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 373 374 stack.pop() 375 return cls 376 377 if _cls is None: 378 return wrap # type: ignore 379 380 if _cls in GENERATION_STACK: 381 return _cls 382 383 GENERATION_STACK.append(_cls) 384 try: 385 return wrap(_cls) 386 finally: 387 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)
342def is_serializable(instance_or_class: Any) -> bool: 343 """ 344 Test if an instance or class is serializable. 345 346 >>> @serialize 347 ... class Foo: 348 ... pass 349 350 Testing `Foo` class object returns `True`. 351 >>> is_serializable(Foo) 352 True 353 354 Testing `Foo` object also returns `True`. 355 >>> is_serializable(Foo()) 356 True 357 """ 358 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
390def is_deserializable(instance_or_class: Any) -> bool: 391 """ 392 Test if an instance or class is deserializable. 393 394 >>> @deserialize 395 ... class Foo: 396 ... pass 397 >>> 398 >>> is_deserializable(Foo) 399 True 400 """ 401 return hasattr(instance_or_class, SERDE_SCOPE)
Test if an instance or class is deserializable.
>>> @deserialize
... class Foo:
... pass
>>>
>>> is_deserializable(Foo)
True
493def to_dict( 494 o: Any, 495 c: type[Any] | None = None, 496 reuse_instances: bool | None = None, 497 convert_sets: bool | None = None, 498 skip_none: bool = False, 499) -> dict[Any, Any]: 500 """ 501 Serialize object into python dictionary. This function ensures that the dataclass's fields are 502 accurately represented as key-value pairs in the resulting dictionary. 503 504 * `o`: Any pyserde object that you want to convert to `dict` 505 * `c`: Optional class argument 506 * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer 507 instead of converting them to serializable representation e.g. string. This behaviour allows 508 to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially 509 improve performance. 510 * `convert_sets`: This option controls how sets are handled during serialization and 511 deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during 512 serialization and back to sets during deserialization. This is useful for data formats that 513 do not natively support sets. 514 * `skip_none`: When set to True, any field in the class with a None value is excluded from the 515 serialized output. Defaults to False. 516 517 >>> from serde import serde 518 >>> @serde 519 ... class Foo: 520 ... i: int 521 ... s: str = 'foo' 522 ... f: float = 100.0 523 ... b: bool = True 524 >>> 525 >>> to_dict(Foo(i=10)) 526 {'i': 10, 's': 'foo', 'f': 100.0, 'b': True} 527 528 You can serialize not only pyserde objects but also objects of any supported types. For example, 529 the following example serializes list of pyserde objects into dict. 530 531 >>> lst = [Foo(i=10), Foo(i=20)] 532 >>> to_dict(lst) 533 [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 534 """ 535 return to_obj( # type: ignore 536 o, 537 named=True, 538 c=c, 539 reuse_instances=reuse_instances, 540 convert_sets=convert_sets, 541 skip_none=skip_none, 542 )
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}]
590def from_dict( 591 cls: Any, 592 o: dict[str, Any], 593 reuse_instances: bool | None = None, 594 deserialize_numbers: Callable[[str | int], float] | None = None, 595) -> Any: 596 """ 597 Deserialize dictionary into object. 598 599 >>> @deserialize 600 ... class Foo: 601 ... i: int 602 ... s: str = 'foo' 603 ... f: float = 100.0 604 ... b: bool = True 605 >>> 606 >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}) 607 Foo(i=10, s='foo', f=100.0, b=True) 608 609 You can pass any type supported by pyserde. For example, 610 611 * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target 612 type is float (e.g. accept ints or numeric strings supplied by a parser). 613 614 >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, 615 ... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 616 >>> from_dict(list[Foo], lst) 617 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 618 """ 619 return from_obj( 620 cls, 621 o, 622 named=True, 623 reuse_instances=reuse_instances, 624 deserialize_numbers=deserialize_numbers, 625 )
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)]
450def to_tuple( 451 o: Any, 452 c: type[Any] | None = None, 453 reuse_instances: bool | None = None, 454 convert_sets: bool | None = None, 455 skip_none: bool = False, 456) -> tuple[Any, ...]: 457 """ 458 Serialize object into tuple. 459 460 >>> @serialize 461 ... class Foo: 462 ... i: int 463 ... s: str = 'foo' 464 ... f: float = 100.0 465 ... b: bool = True 466 >>> 467 >>> to_tuple(Foo(i=10)) 468 (10, 'foo', 100.0, True) 469 470 You can pass any type supported by pyserde. For example, 471 472 >>> lst = [Foo(i=10), Foo(i=20)] 473 >>> to_tuple(lst) 474 [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 475 """ 476 return to_obj( # type: ignore 477 o, 478 named=False, 479 c=c, 480 reuse_instances=reuse_instances, 481 convert_sets=convert_sets, 482 skip_none=skip_none, 483 )
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)]
646def from_tuple( 647 cls: Any, 648 o: Any, 649 reuse_instances: bool | None = None, 650 deserialize_numbers: Callable[[str | int], float] | None = None, 651) -> Any: 652 """ 653 Deserialize tuple into object. 654 655 >>> @deserialize 656 ... class Foo: 657 ... i: int 658 ... s: str = 'foo' 659 ... f: float = 100.0 660 ... b: bool = True 661 >>> 662 >>> from_tuple(Foo, (10, 'foo', 100.0, True)) 663 Foo(i=10, s='foo', f=100.0, b=True) 664 665 You can pass any type supported by pyserde. For example, 666 667 * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target 668 type is float (e.g. accept ints or numeric strings supplied by a parser). 669 670 >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 671 >>> from_tuple(list[Foo], lst) 672 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 673 """ 674 return from_obj( 675 cls, 676 o, 677 named=False, 678 reuse_instances=reuse_instances, 679 deserialize_numbers=deserialize_numbers, 680 )
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.
566def field( 567 *args: Any, 568 rename: str | None = None, 569 alias: list[str] | None = None, 570 skip: bool | None = None, 571 skip_if: Callable[[Any], Any] | None = None, 572 skip_if_false: bool | None = None, 573 skip_if_default: bool | None = None, 574 serializer: Callable[..., Any] | None = None, 575 deserializer: Callable[..., Any] | None = None, 576 flatten: FlattenOpts | bool | None = None, 577 metadata: dict[str, Any] | None = None, 578 **kwargs: Any, 579) -> Any: 580 """ 581 Declare a field with parameters. 582 """ 583 if not metadata: 584 metadata = {} 585 586 if rename is not None: 587 metadata["serde_rename"] = rename 588 if alias is not None: 589 metadata["serde_alias"] = alias 590 if skip is not None: 591 metadata["serde_skip"] = skip 592 if skip_if is not None: 593 metadata["serde_skip_if"] = skip_if 594 if skip_if_false is not None: 595 metadata["serde_skip_if_false"] = skip_if_false 596 if skip_if_default is not None: 597 metadata["serde_skip_if_default"] = skip_if_default 598 if serializer: 599 metadata["serde_serializer"] = serializer 600 if deserializer: 601 metadata["serde_deserializer"] = deserializer 602 if flatten is True: 603 metadata["serde_flatten"] = FlattenOpts() 604 elif flatten: 605 metadata["serde_flatten"] = flatten 606 607 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.
486def asdict(v: Any) -> dict[Any, Any]: 487 """ 488 Serialize object into dictionary. 489 """ 490 return to_dict(v, reuse_instances=False, convert_sets=False)
Serialize object into dictionary.
443def astuple(v: Any) -> tuple[Any, ...]: 444 """ 445 Serialize object into tuple. 446 """ 447 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.
1085class ClassSerializer(Protocol): 1086 """ 1087 Interface for custom class serializer. 1088 1089 This protocol is intended to be used for custom class serializer. 1090 1091 >>> from datetime import datetime 1092 >>> from serde import serde 1093 >>> from plum import dispatch 1094 >>> class MySerializer(ClassSerializer): 1095 ... @dispatch 1096 ... def serialize(self, value: datetime) -> str: 1097 ... return value.strftime("%d/%m/%y") 1098 """ 1099 1100 def serialize(self, value: Any) -> Any: 1101 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)
1104class ClassDeserializer(Protocol): 1105 """ 1106 Interface for custom class deserializer. 1107 1108 This protocol is intended to be used for custom class deserializer. 1109 1110 >>> from datetime import datetime 1111 >>> from serde import serde 1112 >>> from plum import dispatch 1113 >>> class MyDeserializer(ClassDeserializer): 1114 ... @dispatch 1115 ... def deserialize(self, cls: type[datetime], value: Any) -> datetime: 1116 ... return datetime.strptime(value, "%d/%m/%y") 1117 """ 1118 1119 def deserialize(self, cls: Any, value: Any) -> Any: 1120 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)
1128def add_serializer(serializer: ClassSerializer) -> None: 1129 """ 1130 Register custom global serializer. 1131 """ 1132 GLOBAL_CLASS_SERIALIZER.append(serializer)
Register custom global serializer.
1135def add_deserializer(deserializer: ClassDeserializer) -> None: 1136 """ 1137 Register custom global deserializer. 1138 """ 1139 GLOBAL_CLASS_DESERIALIZER.append(deserializer)
Register custom global deserializer.