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
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 serializer: SerializeFunc | None = None, 121 deserializer: DeserializeFunc | None = None, 122 tagging: Tagging = DefaultTagging, 123 type_check: TypeCheck = strict, 124 serialize_class_var: bool = False, 125 class_serializer: ClassSerializer | None = None, 126 class_deserializer: ClassDeserializer | None = None, 127 deny_unknown_fields: bool = False, 128) -> Type[T]: ... 129 130 131@overload 132def serde( 133 _cls: Any = None, 134 rename_all: str | None = None, 135 reuse_instances_default: bool = True, 136 convert_sets_default: bool = False, 137 serializer: SerializeFunc | None = None, 138 deserializer: DeserializeFunc | None = None, 139 tagging: Tagging = DefaultTagging, 140 type_check: TypeCheck = strict, 141 serialize_class_var: bool = False, 142 class_serializer: ClassSerializer | None = None, 143 class_deserializer: ClassDeserializer | None = 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: str | None = None, 152 reuse_instances_default: bool = True, 153 convert_sets_default: bool = False, 154 serializer: SerializeFunc | None = None, 155 deserializer: DeserializeFunc | None = None, 156 tagging: Tagging = DefaultTagging, 157 type_check: TypeCheck = strict, 158 serialize_class_var: bool = False, 159 class_serializer: ClassSerializer | None = None, 160 class_deserializer: ClassDeserializer | None = 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: str | None = None, 153 reuse_instances_default: bool = True, 154 convert_sets_default: bool = False, 155 serializer: SerializeFunc | None = None, 156 deserializer: DeserializeFunc | None = None, 157 tagging: Tagging = DefaultTagging, 158 type_check: TypeCheck = strict, 159 serialize_class_var: bool = False, 160 class_serializer: ClassSerializer | None = None, 161 class_deserializer: ClassDeserializer | None = 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.
188@dataclass_transform() 189def serialize( 190 _cls: type[T] | None = None, 191 rename_all: str | None = None, 192 reuse_instances_default: bool = False, 193 convert_sets_default: bool = False, 194 serializer: SerializeFunc | None = None, 195 tagging: Tagging = DefaultTagging, 196 type_check: TypeCheck = strict, 197 serialize_class_var: bool = False, 198 class_serializer: ClassSerializer | None = None, 199 **kwargs: Any, 200) -> type[T]: 201 """ 202 A dataclass with this decorator is serializable into any of the data formats 203 supported by pyserde. 204 205 >>> from datetime import datetime 206 >>> from serde import serialize 207 >>> from serde.json import to_json 208 >>> 209 >>> @serialize 210 ... class Foo: 211 ... i: int 212 ... s: str 213 ... f: float 214 ... b: bool 215 >>> 216 >>> to_json(Foo(i=10, s='foo', f=100.0, b=True)) 217 '{"i":10,"s":"foo","f":100.0,"b":true}' 218 """ 219 220 def wrap(cls: type[T]) -> type[T]: 221 tagging.check() 222 223 # If no `dataclass` found in the class, dataclassify it automatically. 224 if not is_dataclass(cls): 225 dataclass(cls) 226 227 if type_check.is_strict(): 228 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 229 serde_beartype(cls) 230 231 g: dict[str, Any] = {} 232 233 # Create a scope storage used by serde. 234 # Each class should get own scope. Child classes can not share scope with parent class. 235 # That's why we need the "scope.cls is not cls" check. 236 scope: Scope | None = getattr(cls, SERDE_SCOPE, None) 237 if scope is None or scope.cls is not cls: 238 scope = Scope( 239 cls, 240 reuse_instances_default=reuse_instances_default, 241 convert_sets_default=convert_sets_default, 242 ) 243 setattr(cls, SERDE_SCOPE, scope) 244 245 class_serializers: list[ClassSerializer] = list( 246 itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else []) 247 ) 248 249 # Set some globals for all generated functions 250 g["cls"] = cls 251 g["copy"] = copy 252 g["serde_scope"] = scope 253 g["SerdeError"] = SerdeError 254 g["raise_unsupported_type"] = raise_unsupported_type 255 g["enum_value"] = enum_value 256 g["is_dataclass"] = is_dataclass 257 g["typename"] = typename # used in union functions 258 g["is_instance"] = is_instance # used in union functions 259 g["to_obj"] = to_obj 260 g["typing"] = typing 261 g["Literal"] = Literal 262 g["TypeCheck"] = TypeCheck 263 g["disabled"] = disabled 264 g["coerce_object"] = coerce_object 265 g["class_serializers"] = class_serializers 266 if serializer: 267 g["serde_legacy_custom_class_serializer"] = functools.partial( 268 serde_legacy_custom_class_serializer, custom=serializer 269 ) 270 271 # Collect types used in the generated code. 272 for typ in iter_types(cls): 273 # When we encounter a dataclass not marked with serialize, then also generate serialize 274 # functions for it. 275 if is_dataclass_without_se(typ) and typ is not cls: 276 # We call serialize and not wrap to make sure that we will use the default serde 277 # configuration for generating the serialization function. 278 serialize(typ) 279 280 if is_primitive(typ) and not is_enum(typ): 281 continue 282 g[typename(typ)] = typ 283 284 # render all union functions 285 for union in iter_unions(cls): 286 union_args = list(type_args(union)) 287 union_key = union_func_name(UNION_SE_PREFIX, union_args) 288 add_func(scope, union_key, render_union_func(cls, union_args, tagging), g) 289 scope.union_se_args[union_key] = union_args 290 291 for f in sefields(cls, serialize_class_var): 292 if f.skip_if: 293 g[f.skip_if.name] = f.skip_if 294 if f.serializer: 295 g[f.serializer.name] = f.serializer 296 297 add_func( 298 scope, 299 TO_ITER, 300 render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer), 301 g, 302 ) 303 add_func( 304 scope, 305 TO_DICT, 306 render_to_dict( 307 cls, rename_all, serializer, type_check, serialize_class_var, class_serializer 308 ), 309 g, 310 ) 311 312 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 313 314 return cls 315 316 if _cls is None: 317 return wrap # type: ignore 318 319 if _cls in GENERATION_STACK: 320 return _cls 321 322 GENERATION_STACK.append(_cls) 323 try: 324 return wrap(_cls) 325 finally: 326 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}'
198@dataclass_transform() 199def deserialize( 200 _cls: type[T] | None = None, 201 rename_all: str | None = None, 202 reuse_instances_default: bool = True, 203 convert_sets_default: bool = False, 204 deserializer: DeserializeFunc | None = None, 205 tagging: Tagging = DefaultTagging, 206 type_check: TypeCheck = strict, 207 class_deserializer: ClassDeserializer | None = None, 208 deny_unknown_fields: bool = False, 209 **kwargs: Any, 210) -> type[T]: 211 """ 212 A dataclass with this decorator is deserializable from any of the data formats supported 213 by pyserde. 214 215 >>> from serde import deserialize 216 >>> from serde.json import from_json 217 >>> 218 >>> @deserialize 219 ... class Foo: 220 ... i: int 221 ... s: str 222 ... f: float 223 ... b: bool 224 >>> 225 >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}') 226 Foo(i=10, s='foo', f=100.0, b=True) 227 """ 228 229 stack = [] 230 231 def wrap(cls: type[T]) -> type[T]: 232 if cls in stack: 233 return cls 234 stack.append(cls) 235 236 tagging.check() 237 238 # If no `dataclass` found in the class, dataclassify it automatically. 239 if not is_dataclass(cls): 240 dataclass(cls) 241 242 if type_check.is_strict(): 243 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 244 serde_beartype(cls) 245 246 g: dict[str, Any] = {} 247 248 # Create a scope storage used by serde. 249 # Each class should get own scope. Child classes can not share scope with parent class. 250 # That's why we need the "scope.cls is not cls" check. 251 scope: Scope | None = getattr(cls, SERDE_SCOPE, None) 252 if scope is None or scope.cls is not cls: 253 scope = Scope(cls, reuse_instances_default=reuse_instances_default) 254 setattr(cls, SERDE_SCOPE, scope) 255 256 class_deserializers: list[ClassDeserializer] = list( 257 itertools.chain( 258 GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else [] 259 ) 260 ) 261 262 # Set some globals for all generated functions 263 g["cls"] = cls 264 g["serde_scope"] = scope 265 g["SerdeError"] = SerdeError 266 g["UserError"] = UserError 267 g["raise_unsupported_type"] = raise_unsupported_type 268 g["typename"] = typename 269 g["ensure"] = ensure 270 g["typing"] = typing 271 g["collections"] = collections 272 g["Literal"] = Literal 273 g["from_obj"] = from_obj 274 g["get_generic_arg"] = get_generic_arg 275 g["is_instance"] = is_instance 276 g["TypeCheck"] = TypeCheck 277 g["disabled"] = disabled 278 g["coerce_object"] = coerce_object 279 g["_exists_by_aliases"] = _exists_by_aliases 280 g["_get_by_aliases"] = _get_by_aliases 281 g["class_deserializers"] = class_deserializers 282 g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation 283 g["is_bearable"] = is_bearable 284 if deserializer: 285 g["serde_legacy_custom_class_deserializer"] = functools.partial( 286 serde_legacy_custom_class_deserializer, custom=deserializer 287 ) 288 289 # Collect types used in the generated code. 290 for typ in iter_types(cls): 291 # When we encounter a dataclass not marked with deserialize, then also generate 292 # deserialize functions for it. 293 if is_dataclass_without_de(typ) and typ is not cls: 294 # We call deserialize and not wrap to make sure that we will use the default serde 295 # configuration for generating the deserialization function. 296 deserialize(typ) 297 298 # We don't want to add primitive class e.g "str" into the scope, but primitive 299 # compatible types such as IntEnum and a subclass of primitives are added, 300 # so that generated code can use those types. 301 if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ): 302 continue 303 304 if is_generic(typ): 305 g[typename(typ)] = get_origin(typ) 306 else: 307 g[typename(typ)] = typ 308 309 # render all union functions 310 for union in iter_unions(cls): 311 union_args = type_args(union) 312 add_func( 313 scope, 314 union_func_name(UNION_DE_PREFIX, union_args), 315 render_union_func(cls, union_args, tagging), 316 g, 317 ) 318 319 # render literal functions 320 for literal in iter_literals(cls): 321 literal_args = type_args(literal) 322 add_func( 323 scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g 324 ) 325 326 # Collect default values and default factories used in the generated code. 327 for f in defields(cls): 328 assert f.name 329 if has_default(f): 330 scope.defaults[f.name] = f.default 331 elif has_default_factory(f): 332 scope.defaults[f.name] = f.default_factory 333 if f.deserializer: 334 g[f.deserializer.name] = f.deserializer 335 336 add_func( 337 scope, 338 FROM_ITER, 339 render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer), 340 g, 341 ) 342 add_func( 343 scope, 344 FROM_DICT, 345 render_from_dict( 346 cls, 347 rename_all, 348 deserializer, 349 type_check, 350 class_deserializer=class_deserializer, 351 deny_unknown_fields=deny_unknown_fields, 352 ), 353 g, 354 ) 355 356 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 357 358 stack.pop() 359 return cls 360 361 if _cls is None: 362 return wrap # type: ignore 363 364 if _cls in GENERATION_STACK: 365 return _cls 366 367 GENERATION_STACK.append(_cls) 368 try: 369 return wrap(_cls) 370 finally: 371 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)
329def is_serializable(instance_or_class: Any) -> bool: 330 """ 331 Test if an instance or class is serializable. 332 333 >>> @serialize 334 ... class Foo: 335 ... pass 336 337 Testing `Foo` class object returns `True`. 338 >>> is_serializable(Foo) 339 True 340 341 Testing `Foo` object also returns `True`. 342 >>> is_serializable(Foo()) 343 True 344 """ 345 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
374def is_deserializable(instance_or_class: Any) -> bool: 375 """ 376 Test if an instance or class is deserializable. 377 378 >>> @deserialize 379 ... class Foo: 380 ... pass 381 >>> 382 >>> is_deserializable(Foo) 383 True 384 """ 385 return hasattr(instance_or_class, SERDE_SCOPE)
Test if an instance or class is deserializable.
>>> @deserialize
... class Foo:
... pass
>>>
>>> is_deserializable(Foo)
True
476def to_dict( 477 o: Any, 478 c: type[Any] | None = None, 479 reuse_instances: bool | None = None, 480 convert_sets: bool | None = None, 481 skip_none: bool = False, 482) -> dict[Any, Any]: 483 """ 484 Serialize object into python dictionary. This function ensures that the dataclass's fields are 485 accurately represented as key-value pairs in the resulting dictionary. 486 487 * `o`: Any pyserde object that you want to convert to `dict` 488 * `c`: Optional class argument 489 * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer 490 instead of converting them to serializable representation e.g. string. This behaviour allows 491 to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially 492 improve performance. 493 * `convert_sets`: This option controls how sets are handled during serialization and 494 deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during 495 serialization and back to sets during deserialization. This is useful for data formats that 496 do not natively support sets. 497 * `skip_none`: When set to True, any field in the class with a None value is excluded from the 498 serialized output. Defaults to False. 499 500 >>> from serde import serde 501 >>> @serde 502 ... class Foo: 503 ... i: int 504 ... s: str = 'foo' 505 ... f: float = 100.0 506 ... b: bool = True 507 >>> 508 >>> to_dict(Foo(i=10)) 509 {'i': 10, 's': 'foo', 'f': 100.0, 'b': True} 510 511 You can serialize not only pyserde objects but also objects of any supported types. For example, 512 the following example serializes list of pyserde objects into dict. 513 514 >>> lst = [Foo(i=10), Foo(i=20)] 515 >>> to_dict(lst) 516 [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 517 """ 518 return to_obj( # type: ignore 519 o, 520 named=True, 521 c=c, 522 reuse_instances=reuse_instances, 523 convert_sets=convert_sets, 524 skip_none=skip_none, 525 )
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}]
564def from_dict( 565 cls: Any, 566 o: dict[str, Any], 567 reuse_instances: bool | None = None, 568 deserialize_numbers: Callable[[str | int], float] | None = None, 569) -> Any: 570 """ 571 Deserialize dictionary into object. 572 573 >>> @deserialize 574 ... class Foo: 575 ... i: int 576 ... s: str = 'foo' 577 ... f: float = 100.0 578 ... b: bool = True 579 >>> 580 >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}) 581 Foo(i=10, s='foo', f=100.0, b=True) 582 583 You can pass any type supported by pyserde. For example, 584 585 * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target 586 type is float (e.g. accept ints or numeric strings supplied by a parser). 587 588 >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, 589 ... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 590 >>> from_dict(list[Foo], lst) 591 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 592 """ 593 return from_obj( 594 cls, 595 o, 596 named=True, 597 reuse_instances=reuse_instances, 598 deserialize_numbers=deserialize_numbers, 599 )
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)]
433def to_tuple( 434 o: Any, 435 c: type[Any] | None = None, 436 reuse_instances: bool | None = None, 437 convert_sets: bool | None = None, 438 skip_none: bool = False, 439) -> tuple[Any, ...]: 440 """ 441 Serialize object into tuple. 442 443 >>> @serialize 444 ... class Foo: 445 ... i: int 446 ... s: str = 'foo' 447 ... f: float = 100.0 448 ... b: bool = True 449 >>> 450 >>> to_tuple(Foo(i=10)) 451 (10, 'foo', 100.0, True) 452 453 You can pass any type supported by pyserde. For example, 454 455 >>> lst = [Foo(i=10), Foo(i=20)] 456 >>> to_tuple(lst) 457 [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 458 """ 459 return to_obj( # type: ignore 460 o, 461 named=False, 462 c=c, 463 reuse_instances=reuse_instances, 464 convert_sets=convert_sets, 465 skip_none=skip_none, 466 )
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)]
620def from_tuple( 621 cls: Any, 622 o: Any, 623 reuse_instances: bool | None = None, 624 deserialize_numbers: Callable[[str | int], float] | None = None, 625) -> Any: 626 """ 627 Deserialize tuple into object. 628 629 >>> @deserialize 630 ... class Foo: 631 ... i: int 632 ... s: str = 'foo' 633 ... f: float = 100.0 634 ... b: bool = True 635 >>> 636 >>> from_tuple(Foo, (10, 'foo', 100.0, True)) 637 Foo(i=10, s='foo', f=100.0, b=True) 638 639 You can pass any type supported by pyserde. For example, 640 641 * `deserialize_numbers`: Optional callable to coerce numeric input to floats when the target 642 type is float (e.g. accept ints or numeric strings supplied by a parser). 643 644 >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 645 >>> from_tuple(list[Foo], lst) 646 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 647 """ 648 return from_obj( 649 cls, 650 o, 651 named=False, 652 reuse_instances=reuse_instances, 653 deserialize_numbers=deserialize_numbers, 654 )
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.
533def field( 534 *args: Any, 535 rename: str | None = None, 536 alias: list[str] | None = None, 537 skip: bool | None = None, 538 skip_if: Callable[[Any], Any] | None = None, 539 skip_if_false: bool | None = None, 540 skip_if_default: bool | None = None, 541 serializer: Callable[..., Any] | None = None, 542 deserializer: Callable[..., Any] | None = None, 543 flatten: FlattenOpts | bool | None = None, 544 metadata: dict[str, Any] | None = None, 545 **kwargs: Any, 546) -> Any: 547 """ 548 Declare a field with parameters. 549 """ 550 if not metadata: 551 metadata = {} 552 553 if rename is not None: 554 metadata["serde_rename"] = rename 555 if alias is not None: 556 metadata["serde_alias"] = alias 557 if skip is not None: 558 metadata["serde_skip"] = skip 559 if skip_if is not None: 560 metadata["serde_skip_if"] = skip_if 561 if skip_if_false is not None: 562 metadata["serde_skip_if_false"] = skip_if_false 563 if skip_if_default is not None: 564 metadata["serde_skip_if_default"] = skip_if_default 565 if serializer: 566 metadata["serde_serializer"] = serializer 567 if deserializer: 568 metadata["serde_deserializer"] = deserializer 569 if flatten is True: 570 metadata["serde_flatten"] = FlattenOpts() 571 elif flatten: 572 metadata["serde_flatten"] = flatten 573 574 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.
469def asdict(v: Any) -> dict[Any, Any]: 470 """ 471 Serialize object into dictionary. 472 """ 473 return to_dict(v, reuse_instances=False, convert_sets=False)
Serialize object into dictionary.
426def astuple(v: Any) -> tuple[Any, ...]: 427 """ 428 Serialize object into tuple. 429 """ 430 return to_tuple(v, reuse_instances=False, convert_sets=False)
Serialize object into tuple.
121def default_serializer(_cls: type[Any], obj: Any) -> Any: 122 """ 123 Marker function to tell serde to use the default serializer. It's used when custom serializer 124 is specified at the class but you want to override a field with the default serializer. 125 """
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.
1018class ClassSerializer(Protocol): 1019 """ 1020 Interface for custom class serializer. 1021 1022 This protocol is intended to be used for custom class serializer. 1023 1024 >>> from datetime import datetime 1025 >>> from serde import serde 1026 >>> from plum import dispatch 1027 >>> class MySerializer(ClassSerializer): 1028 ... @dispatch 1029 ... def serialize(self, value: datetime) -> str: 1030 ... return value.strftime("%d/%m/%y") 1031 """ 1032 1033 def serialize(self, value: Any) -> Any: 1034 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)
1037class ClassDeserializer(Protocol): 1038 """ 1039 Interface for custom class deserializer. 1040 1041 This protocol is intended to be used for custom class deserializer. 1042 1043 >>> from datetime import datetime 1044 >>> from serde import serde 1045 >>> from plum import dispatch 1046 >>> class MyDeserializer(ClassDeserializer): 1047 ... @dispatch 1048 ... def deserialize(self, cls: type[datetime], value: Any) -> datetime: 1049 ... return datetime.strptime(value, "%d/%m/%y") 1050 """ 1051 1052 def deserialize(self, cls: Any, value: Any) -> Any: 1053 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)
1061def add_serializer(serializer: ClassSerializer) -> None: 1062 """ 1063 Register custom global serializer. 1064 """ 1065 GLOBAL_CLASS_SERIALIZER.append(serializer)
Register custom global serializer.
1068def add_deserializer(deserializer: ClassDeserializer) -> None: 1069 """ 1070 Register custom global deserializer. 1071 """ 1072 GLOBAL_CLASS_DESERIALIZER.append(deserializer)
Register custom global deserializer.