serde
pyserde
Yet another serialization library on top of dataclasses, inspired by serde-rs.
Guideπ¬π§ | γ¬γ€γπ―π΅ | API Reference | Examples
Overview
pyserde
is a simple yet powerful serialization library on top of dataclasses. It allows you to convert Python objects to and from JSON, YAML, and other formats easily and efficiently.
Declare your class with @serde
decorator and annotate fields using PEP484 as below.
@serde
class Foo:
i: int
s: str
f: float
b: bool
You can serialize Foo
object into JSON.
>>> to_json(Foo(i=10, s='foo', f=100.0, b=True))
'{"i":10,"s":"foo","f":100.0,"b":true}'
You can deserialize JSON into Foo
object.
>>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}')
Foo(i=10, s='foo', f=100.0, b=True)
That's it! If you're interested in pyserde, please check our documentation! Happy coding with pyserde! π
Features
- Supported data formats
- dict
- tuple
- JSON
- Yaml
- Toml
- MsgPack
- Pickle
- Supported types
- Primitives (
int
,float
,str
,bool
) - Containers
list
,set
,tuple
,dict
frozenset
,defaultdict
typing.Optional
typing.Union
- User defined class with
@dataclass
typing.NewType
for primitive typestyping.Any
typing.Literal
typing.Generic
typing.ClassVar
dataclasses.InitVar
Enum
andIntEnum
- Standard library
- PyPI library
numpy
typesSQLAlchemy
Declarative Dataclass Mapping (experimental)
- Primitives (
- Class Attributes
- Field Attributes
- Decorators
- Type Check
- Union Representation
- Forward reference
- PEP563 Postponed Evaluation of Annotations
- PEP585 Type Hinting Generics In Standard Collections
- PEP604 Allow writing union types as X | Y
- PEP681 Data Class Transform
- Case Conversion
- Rename
- Alias
- Skip (de)serialization (skip, skip_if, skip_if_false, skip_if_default)
- Custom field (de)serializer
- Custom class (de)serializer
- Custom global (de)serializer
- Flatten
Extensions
- pyserde-timedelta: (de)serializing datetime.timedelta in ISO 8601 duration format.
Contributors β¨
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
LICENSE
This project is licensed under the MIT license.
Modules
The following modules provide the core functionalities of pyserde
.
serde.se
: All about serialization.serde.de
: All about deserialization.serde.core
: Core module used byserde.se
andserde.de
modules.serde.compat
: Compatibility layer which handles mostly differences oftyping
module between python versions.
The following modules provide pyserde's (de)serialize APIs.
serde.json
: Serialize and Deserialize in JSON.serde.msgpack
: Serialize and Deserialize in MsgPack.serde.yaml
: Serialize and Deserialize in YAML.serde.toml
: Serialize and Deserialize in TOML.serde.pickle
: Serialize and Deserialize in Pickle.
Other modules
serde.inspect
: Prints generated code by pyserde.
1""" 2.. include:: ../README.md 3 4## Modules 5 6The following modules provide the core functionalities of `pyserde`. 7* `serde.se`: All about serialization. 8* `serde.de`: All about deserialization. 9* `serde.core`: Core module used by `serde.se` and `serde.de` modules. 10* `serde.compat`: Compatibility layer which handles mostly differences of `typing` module between 11python versions. 12 13The following modules provide pyserde's (de)serialize APIs. 14* `serde.json`: Serialize and Deserialize in JSON. 15* `serde.msgpack`: Serialize and Deserialize in MsgPack. 16* `serde.yaml`: Serialize and Deserialize in YAML. 17* `serde.toml`: Serialize and Deserialize in TOML. 18* `serde.pickle`: Serialize and Deserialize in Pickle. 19 20Other modules 21* `serde.inspect`: Prints generated code by pyserde. 22""" 23 24from dataclasses import dataclass 25from collections.abc import Callable 26from typing import Optional, overload, Any, Type 27 28from typing_extensions import dataclass_transform 29 30from .compat import SerdeError, SerdeSkip, T 31from .core import ( 32 ClassSerializer, 33 ClassDeserializer, 34 AdjacentTagging, 35 coerce, 36 DefaultTagging, 37 ExternalTagging, 38 InternalTagging, 39 disabled, 40 strict, 41 Tagging, 42 TypeCheck, 43 Untagged, 44 field, 45 init, 46 logger, 47 should_impl_dataclass, 48 add_serializer, 49 add_deserializer, 50) 51from .de import ( 52 DeserializeFunc, 53 default_deserializer, 54 deserialize, 55 from_dict, 56 from_tuple, 57 is_deserializable, 58) 59from .se import ( 60 SerializeFunc, 61 asdict, 62 astuple, 63 default_serializer, 64 is_serializable, 65 serialize, 66 to_dict, 67 to_tuple, 68) 69 70__all__ = [ 71 "serde", 72 "serialize", 73 "deserialize", 74 "is_serializable", 75 "is_deserializable", 76 "to_dict", 77 "from_dict", 78 "to_tuple", 79 "from_tuple", 80 "SerdeError", 81 "SerdeSkip", 82 "AdjacentTagging", 83 "ExternalTagging", 84 "InternalTagging", 85 "Untagged", 86 "disabled", 87 "strict", 88 "coerce", 89 "field", 90 "default_deserializer", 91 "asdict", 92 "astuple", 93 "default_serializer", 94 "compat", 95 "core", 96 "de", 97 "inspect", 98 "json", 99 "msgpack", 100 "numpy", 101 "se", 102 "toml", 103 "pickle", 104 "yaml", 105 "init", 106 "logger", 107 "ClassSerializer", 108 "ClassDeserializer", 109 "add_serializer", 110 "add_deserializer", 111] 112 113 114@overload 115def serde( 116 _cls: Type[T], 117 rename_all: Optional[str] = None, 118 reuse_instances_default: bool = True, 119 convert_sets_default: bool = False, 120 serializer: Optional[SerializeFunc] = None, 121 deserializer: Optional[DeserializeFunc] = None, 122 tagging: Tagging = DefaultTagging, 123 type_check: TypeCheck = strict, 124 serialize_class_var: bool = False, 125 class_serializer: Optional[ClassSerializer] = None, 126 class_deserializer: Optional[ClassDeserializer] = None, 127 deny_unknown_fields: bool = False, 128) -> Type[T]: ... 129 130 131@overload 132def serde( 133 _cls: Any = None, 134 rename_all: Optional[str] = None, 135 reuse_instances_default: bool = True, 136 convert_sets_default: bool = False, 137 serializer: Optional[SerializeFunc] = None, 138 deserializer: Optional[DeserializeFunc] = None, 139 tagging: Tagging = DefaultTagging, 140 type_check: TypeCheck = strict, 141 serialize_class_var: bool = False, 142 class_serializer: Optional[ClassSerializer] = None, 143 class_deserializer: Optional[ClassDeserializer] = None, 144 deny_unknown_fields: bool = False, 145) -> Callable[[type[T]], type[T]]: ... 146 147 148@dataclass_transform(field_specifiers=(field,)) 149def serde( 150 _cls: Any = None, 151 rename_all: Optional[str] = None, 152 reuse_instances_default: bool = True, 153 convert_sets_default: bool = False, 154 serializer: Optional[SerializeFunc] = None, 155 deserializer: Optional[DeserializeFunc] = None, 156 tagging: Tagging = DefaultTagging, 157 type_check: TypeCheck = strict, 158 serialize_class_var: bool = False, 159 class_serializer: Optional[ClassSerializer] = None, 160 class_deserializer: Optional[ClassDeserializer] = None, 161 deny_unknown_fields: bool = False, 162) -> Any: 163 """ 164 serde decorator. Keyword arguments are passed in `serialize` and `deserialize`. 165 """ 166 167 def wrap(cls: Any) -> Any: 168 if should_impl_dataclass(cls): 169 dataclass(cls) 170 serialize( 171 cls, 172 rename_all=rename_all, 173 reuse_instances_default=reuse_instances_default, 174 convert_sets_default=convert_sets_default, 175 serializer=serializer, 176 deserializer=deserializer, 177 tagging=tagging, 178 type_check=type_check, 179 serialize_class_var=serialize_class_var, 180 class_serializer=class_serializer, 181 ) 182 deserialize( 183 cls, 184 rename_all=rename_all, 185 reuse_instances_default=reuse_instances_default, 186 convert_sets_default=convert_sets_default, 187 serializer=serializer, 188 deserializer=deserializer, 189 tagging=tagging, 190 type_check=type_check, 191 serialize_class_var=serialize_class_var, 192 class_deserializer=class_deserializer, 193 deny_unknown_fields=deny_unknown_fields, 194 ) 195 return cls 196 197 if _cls is None: 198 return wrap 199 200 return wrap(_cls)
149@dataclass_transform(field_specifiers=(field,)) 150def serde( 151 _cls: Any = None, 152 rename_all: Optional[str] = None, 153 reuse_instances_default: bool = True, 154 convert_sets_default: bool = False, 155 serializer: Optional[SerializeFunc] = None, 156 deserializer: Optional[DeserializeFunc] = None, 157 tagging: Tagging = DefaultTagging, 158 type_check: TypeCheck = strict, 159 serialize_class_var: bool = False, 160 class_serializer: Optional[ClassSerializer] = None, 161 class_deserializer: Optional[ClassDeserializer] = None, 162 deny_unknown_fields: bool = False, 163) -> Any: 164 """ 165 serde decorator. Keyword arguments are passed in `serialize` and `deserialize`. 166 """ 167 168 def wrap(cls: Any) -> Any: 169 if should_impl_dataclass(cls): 170 dataclass(cls) 171 serialize( 172 cls, 173 rename_all=rename_all, 174 reuse_instances_default=reuse_instances_default, 175 convert_sets_default=convert_sets_default, 176 serializer=serializer, 177 deserializer=deserializer, 178 tagging=tagging, 179 type_check=type_check, 180 serialize_class_var=serialize_class_var, 181 class_serializer=class_serializer, 182 ) 183 deserialize( 184 cls, 185 rename_all=rename_all, 186 reuse_instances_default=reuse_instances_default, 187 convert_sets_default=convert_sets_default, 188 serializer=serializer, 189 deserializer=deserializer, 190 tagging=tagging, 191 type_check=type_check, 192 serialize_class_var=serialize_class_var, 193 class_deserializer=class_deserializer, 194 deny_unknown_fields=deny_unknown_fields, 195 ) 196 return cls 197 198 if _cls is None: 199 return wrap 200 201 return wrap(_cls)
serde decorator. Keyword arguments are passed in serialize
and deserialize
.
162@dataclass_transform() 163def serialize( 164 _cls: Optional[type[T]] = None, 165 rename_all: Optional[str] = None, 166 reuse_instances_default: bool = False, 167 convert_sets_default: bool = False, 168 serializer: Optional[SerializeFunc] = None, 169 tagging: Tagging = DefaultTagging, 170 type_check: TypeCheck = strict, 171 serialize_class_var: bool = False, 172 class_serializer: Optional[ClassSerializer] = None, 173 **kwargs: Any, 174) -> type[T]: 175 """ 176 A dataclass with this decorator is serializable into any of the data formats 177 supported by pyserde. 178 179 >>> from datetime import datetime 180 >>> from serde import serialize 181 >>> from serde.json import to_json 182 >>> 183 >>> @serialize 184 ... class Foo: 185 ... i: int 186 ... s: str 187 ... f: float 188 ... b: bool 189 >>> 190 >>> to_json(Foo(i=10, s='foo', f=100.0, b=True)) 191 '{"i":10,"s":"foo","f":100.0,"b":true}' 192 """ 193 194 def wrap(cls: type[T]) -> type[T]: 195 tagging.check() 196 197 # If no `dataclass` found in the class, dataclassify it automatically. 198 if not is_dataclass(cls): 199 dataclass(cls) 200 201 if type_check.is_strict(): 202 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 203 serde_beartype(cls) 204 205 g: dict[str, Any] = {} 206 207 # Create a scope storage used by serde. 208 # Each class should get own scope. Child classes can not share scope with parent class. 209 # That's why we need the "scope.cls is not cls" check. 210 scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None) 211 if scope is None or scope.cls is not cls: 212 scope = Scope( 213 cls, 214 reuse_instances_default=reuse_instances_default, 215 convert_sets_default=convert_sets_default, 216 ) 217 setattr(cls, SERDE_SCOPE, scope) 218 219 class_serializers: list[ClassSerializer] = list( 220 itertools.chain(GLOBAL_CLASS_SERIALIZER, [class_serializer] if class_serializer else []) 221 ) 222 223 # Set some globals for all generated functions 224 g["cls"] = cls 225 g["copy"] = copy 226 g["serde_scope"] = scope 227 g["SerdeError"] = SerdeError 228 g["raise_unsupported_type"] = raise_unsupported_type 229 g["enum_value"] = enum_value 230 g["is_dataclass"] = is_dataclass 231 g["typename"] = typename # used in union functions 232 g["is_instance"] = is_instance # used in union functions 233 g["to_obj"] = to_obj 234 g["typing"] = typing 235 g["Literal"] = Literal 236 g["TypeCheck"] = TypeCheck 237 g["disabled"] = disabled 238 g["coerce_object"] = coerce_object 239 g["class_serializers"] = class_serializers 240 if serializer: 241 g["serde_legacy_custom_class_serializer"] = functools.partial( 242 serde_legacy_custom_class_serializer, custom=serializer 243 ) 244 245 # Collect types used in the generated code. 246 for typ in iter_types(cls): 247 # When we encounter a dataclass not marked with serialize, then also generate serialize 248 # functions for it. 249 if is_dataclass_without_se(typ) and typ is not cls: 250 # We call serialize and not wrap to make sure that we will use the default serde 251 # configuration for generating the serialization function. 252 serialize(typ) 253 254 if is_primitive(typ) and not is_enum(typ): 255 continue 256 g[typename(typ)] = typ 257 258 # render all union functions 259 for union in iter_unions(cls): 260 union_args = list(type_args(union)) 261 union_key = union_func_name(UNION_SE_PREFIX, union_args) 262 add_func(scope, union_key, render_union_func(cls, union_args, tagging), g) 263 scope.union_se_args[union_key] = union_args 264 265 for f in sefields(cls, serialize_class_var): 266 if f.skip_if: 267 g[f.skip_if.name] = f.skip_if 268 if f.serializer: 269 g[f.serializer.name] = f.serializer 270 271 add_func( 272 scope, 273 TO_ITER, 274 render_to_tuple(cls, serializer, type_check, serialize_class_var, class_serializer), 275 g, 276 ) 277 add_func( 278 scope, 279 TO_DICT, 280 render_to_dict( 281 cls, rename_all, serializer, type_check, serialize_class_var, class_serializer 282 ), 283 g, 284 ) 285 286 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 287 288 return cls 289 290 if _cls is None: 291 return wrap # type: ignore 292 293 if _cls in GENERATION_STACK: 294 return _cls 295 296 GENERATION_STACK.append(_cls) 297 try: 298 return wrap(_cls) 299 finally: 300 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}'
186@dataclass_transform() 187def deserialize( 188 _cls: Optional[type[T]] = None, 189 rename_all: Optional[str] = None, 190 reuse_instances_default: bool = True, 191 convert_sets_default: bool = False, 192 deserializer: Optional[DeserializeFunc] = None, 193 tagging: Tagging = DefaultTagging, 194 type_check: TypeCheck = strict, 195 class_deserializer: Optional[ClassDeserializer] = None, 196 deny_unknown_fields: bool = False, 197 **kwargs: Any, 198) -> type[T]: 199 """ 200 A dataclass with this decorator is deserializable from any of the data formats supported 201 by pyserde. 202 203 >>> from serde import deserialize 204 >>> from serde.json import from_json 205 >>> 206 >>> @deserialize 207 ... class Foo: 208 ... i: int 209 ... s: str 210 ... f: float 211 ... b: bool 212 >>> 213 >>> from_json(Foo, '{"i": 10, "s": "foo", "f": 100.0, "b": true}') 214 Foo(i=10, s='foo', f=100.0, b=True) 215 """ 216 217 stack = [] 218 219 def wrap(cls: type[T]) -> type[T]: 220 if cls in stack: 221 return cls 222 stack.append(cls) 223 224 tagging.check() 225 226 # If no `dataclass` found in the class, dataclassify it automatically. 227 if not is_dataclass(cls): 228 dataclass(cls) 229 230 if type_check.is_strict(): 231 serde_beartype = beartype(conf=BeartypeConf(violation_type=SerdeError)) 232 serde_beartype(cls) 233 234 g: dict[str, Any] = {} 235 236 # Create a scope storage used by serde. 237 # Each class should get own scope. Child classes can not share scope with parent class. 238 # That's why we need the "scope.cls is not cls" check. 239 scope: Optional[Scope] = getattr(cls, SERDE_SCOPE, None) 240 if scope is None or scope.cls is not cls: 241 scope = Scope(cls, reuse_instances_default=reuse_instances_default) 242 setattr(cls, SERDE_SCOPE, scope) 243 244 class_deserializers: list[ClassDeserializer] = list( 245 itertools.chain( 246 GLOBAL_CLASS_DESERIALIZER, [class_deserializer] if class_deserializer else [] 247 ) 248 ) 249 250 # Set some globals for all generated functions 251 g["cls"] = cls 252 g["serde_scope"] = scope 253 g["SerdeError"] = SerdeError 254 g["UserError"] = UserError 255 g["raise_unsupported_type"] = raise_unsupported_type 256 g["typename"] = typename 257 g["ensure"] = ensure 258 g["typing"] = typing 259 g["collections"] = collections 260 g["Literal"] = Literal 261 g["from_obj"] = from_obj 262 g["get_generic_arg"] = get_generic_arg 263 g["is_instance"] = is_instance 264 g["TypeCheck"] = TypeCheck 265 g["disabled"] = disabled 266 g["coerce_object"] = coerce_object 267 g["_exists_by_aliases"] = _exists_by_aliases 268 g["_get_by_aliases"] = _get_by_aliases 269 g["class_deserializers"] = class_deserializers 270 g["BeartypeCallHintParamViolation"] = BeartypeCallHintParamViolation 271 g["is_bearable"] = is_bearable 272 if deserializer: 273 g["serde_legacy_custom_class_deserializer"] = functools.partial( 274 serde_legacy_custom_class_deserializer, custom=deserializer 275 ) 276 277 # Collect types used in the generated code. 278 for typ in iter_types(cls): 279 # When we encounter a dataclass not marked with deserialize, then also generate 280 # deserialize functions for it. 281 if is_dataclass_without_de(typ) and typ is not cls: 282 # We call deserialize and not wrap to make sure that we will use the default serde 283 # configuration for generating the deserialization function. 284 deserialize(typ) 285 286 # We don't want to add primitive class e.g "str" into the scope, but primitive 287 # compatible types such as IntEnum and a subclass of primitives are added, 288 # so that generated code can use those types. 289 if is_primitive(typ) and not is_enum(typ) and not is_primitive_subclass(typ): 290 continue 291 292 if is_generic(typ): 293 g[typename(typ)] = get_origin(typ) 294 else: 295 g[typename(typ)] = typ 296 297 # render all union functions 298 for union in iter_unions(cls): 299 union_args = type_args(union) 300 add_func( 301 scope, 302 union_func_name(UNION_DE_PREFIX, union_args), 303 render_union_func(cls, union_args, tagging), 304 g, 305 ) 306 307 # render literal functions 308 for literal in iter_literals(cls): 309 literal_args = type_args(literal) 310 add_func( 311 scope, literal_func_name(literal_args), render_literal_func(cls, literal_args), g 312 ) 313 314 # Collect default values and default factories used in the generated code. 315 for f in defields(cls): 316 assert f.name 317 if has_default(f): 318 scope.defaults[f.name] = f.default 319 elif has_default_factory(f): 320 scope.defaults[f.name] = f.default_factory 321 if f.deserializer: 322 g[f.deserializer.name] = f.deserializer 323 324 add_func( 325 scope, 326 FROM_ITER, 327 render_from_iter(cls, deserializer, type_check, class_deserializer=class_deserializer), 328 g, 329 ) 330 add_func( 331 scope, 332 FROM_DICT, 333 render_from_dict( 334 cls, 335 rename_all, 336 deserializer, 337 type_check, 338 class_deserializer=class_deserializer, 339 deny_unknown_fields=deny_unknown_fields, 340 ), 341 g, 342 ) 343 344 logger.debug(f"{typename(cls)}: {SERDE_SCOPE} {scope}") 345 346 stack.pop() 347 return cls 348 349 if _cls is None: 350 return wrap # type: ignore 351 352 if _cls in GENERATION_STACK: 353 return _cls 354 355 GENERATION_STACK.append(_cls) 356 try: 357 return wrap(_cls) 358 finally: 359 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)
303def is_serializable(instance_or_class: Any) -> bool: 304 """ 305 Test if an instance or class is serializable. 306 307 >>> @serialize 308 ... class Foo: 309 ... pass 310 311 Testing `Foo` class object returns `True`. 312 >>> is_serializable(Foo) 313 True 314 315 Testing `Foo` object also returns `True`. 316 >>> is_serializable(Foo()) 317 True 318 """ 319 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
362def is_deserializable(instance_or_class: Any) -> bool: 363 """ 364 Test if an instance or class is deserializable. 365 366 >>> @deserialize 367 ... class Foo: 368 ... pass 369 >>> 370 >>> is_deserializable(Foo) 371 True 372 """ 373 return hasattr(instance_or_class, SERDE_SCOPE)
Test if an instance or class is deserializable.
>>> @deserialize
... class Foo:
... pass
>>>
>>> is_deserializable(Foo)
True
449def to_dict( 450 o: Any, 451 c: Optional[type[Any]] = None, 452 reuse_instances: Optional[bool] = None, 453 convert_sets: Optional[bool] = None, 454 skip_none: bool = False, 455) -> dict[Any, Any]: 456 """ 457 Serialize object into python dictionary. This function ensures that the dataclass's fields are 458 accurately represented as key-value pairs in the resulting dictionary. 459 460 * `o`: Any pyserde object that you want to convert to `dict` 461 * `c`: Optional class argument 462 * `reuse_instances`: pyserde will pass instances (e.g. Path, datetime) directly to serializer 463 instead of converting them to serializable representation e.g. string. This behaviour allows 464 to delegate serializtation to underlying data format packages e.g. `pyyaml` and potentially 465 improve performance. 466 * `convert_sets`: This option controls how sets are handled during serialization and 467 deserialization. When `convert_sets` is set to True, pyserde will convert sets to lists during 468 serialization and back to sets during deserialization. This is useful for data formats that 469 do not natively support sets. 470 * `skip_none`: When set to True, any field in the class with a None value is excluded from the 471 serialized output. Defaults to False. 472 473 >>> from serde import serde 474 >>> @serde 475 ... class Foo: 476 ... i: int 477 ... s: str = 'foo' 478 ... f: float = 100.0 479 ... b: bool = True 480 >>> 481 >>> to_dict(Foo(i=10)) 482 {'i': 10, 's': 'foo', 'f': 100.0, 'b': True} 483 484 You can serialize not only pyserde objects but also objects of any supported types. For example, 485 the following example serializes list of pyserde objects into dict. 486 487 >>> lst = [Foo(i=10), Foo(i=20)] 488 >>> to_dict(lst) 489 [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 490 """ 491 return to_obj( # type: ignore 492 o, 493 named=True, 494 c=c, 495 reuse_instances=reuse_instances, 496 convert_sets=convert_sets, 497 skip_none=skip_none, 498 )
Serialize object into python dictionary. This function ensures that the dataclass's fields are accurately represented as key-value pairs in the resulting dictionary.
o
: Any pyserde object that you want to convert todict
c
: Optional class argumentreuse_instances
: pyserde will pass instances (e.g. Path, datetime) directly to serializer instead of converting them to serializable representation e.g. string. This behaviour allows to delegate serializtation to underlying data format packages e.g.pyyaml
and potentially improve performance.convert_sets
: This option controls how sets are handled during serialization and deserialization. Whenconvert_sets
is set to True, pyserde will convert sets to lists during serialization and back to sets during deserialization. This is useful for data formats that do not natively support sets.skip_none
: When set to True, any field in the class with a None value is excluded from the serialized output. Defaults to False.
>>> from serde import serde
>>> @serde
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> to_dict(Foo(i=10))
{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}
You can serialize not only pyserde objects but also objects of any supported types. For example, the following example serializes list of pyserde objects into dict.
>>> lst = [Foo(i=10), Foo(i=20)]
>>> to_dict(lst)
[{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
519def from_dict(cls: Any, o: dict[str, Any], reuse_instances: Optional[bool] = None) -> Any: 520 """ 521 Deserialize dictionary into object. 522 523 >>> @deserialize 524 ... class Foo: 525 ... i: int 526 ... s: str = 'foo' 527 ... f: float = 100.0 528 ... b: bool = True 529 >>> 530 >>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True}) 531 Foo(i=10, s='foo', f=100.0, b=True) 532 533 You can pass any type supported by pyserde. For example, 534 535 >>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True}, 536 ... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}] 537 >>> from_dict(list[Foo], lst) 538 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 539 """ 540 return from_obj(cls, o, named=True, reuse_instances=reuse_instances)
Deserialize dictionary into object.
>>> @deserialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> from_dict(Foo, {'i': 10, 's': 'foo', 'f': 100.0, 'b': True})
Foo(i=10, s='foo', f=100.0, b=True)
You can pass any type supported by pyserde. For example,
>>> lst = [{'i': 10, 's': 'foo', 'f': 100.0, 'b': True},
... {'i': 20, 's': 'foo', 'f': 100.0, 'b': True}]
>>> from_dict(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
406def to_tuple( 407 o: Any, 408 c: Optional[type[Any]] = None, 409 reuse_instances: Optional[bool] = None, 410 convert_sets: Optional[bool] = None, 411 skip_none: bool = False, 412) -> tuple[Any, ...]: 413 """ 414 Serialize object into tuple. 415 416 >>> @serialize 417 ... class Foo: 418 ... i: int 419 ... s: str = 'foo' 420 ... f: float = 100.0 421 ... b: bool = True 422 >>> 423 >>> to_tuple(Foo(i=10)) 424 (10, 'foo', 100.0, True) 425 426 You can pass any type supported by pyserde. For example, 427 428 >>> lst = [Foo(i=10), Foo(i=20)] 429 >>> to_tuple(lst) 430 [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 431 """ 432 return to_obj( # type: ignore 433 o, 434 named=False, 435 c=c, 436 reuse_instances=reuse_instances, 437 convert_sets=convert_sets, 438 skip_none=skip_none, 439 )
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)]
551def from_tuple(cls: Any, o: Any, reuse_instances: Optional[bool] = None) -> Any: 552 """ 553 Deserialize tuple into object. 554 555 >>> @deserialize 556 ... class Foo: 557 ... i: int 558 ... s: str = 'foo' 559 ... f: float = 100.0 560 ... b: bool = True 561 >>> 562 >>> from_tuple(Foo, (10, 'foo', 100.0, True)) 563 Foo(i=10, s='foo', f=100.0, b=True) 564 565 You can pass any type supported by pyserde. For example, 566 567 >>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)] 568 >>> from_tuple(list[Foo], lst) 569 [Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)] 570 """ 571 return from_obj(cls, o, named=False, reuse_instances=reuse_instances)
Deserialize tuple into object.
>>> @deserialize
... class Foo:
... i: int
... s: str = 'foo'
... f: float = 100.0
... b: bool = True
>>>
>>> from_tuple(Foo, (10, 'foo', 100.0, True))
Foo(i=10, s='foo', f=100.0, b=True)
You can pass any type supported by pyserde. For example,
>>> lst = [(10, 'foo', 100.0, True), (20, 'foo', 100.0, True)]
>>> from_tuple(list[Foo], lst)
[Foo(i=10, s='foo', f=100.0, b=True), Foo(i=20, s='foo', f=100.0, b=True)]
Serde error class.
Skip a field in custom (de)serializer.
515def field( 516 *args: Any, 517 rename: Optional[str] = None, 518 alias: Optional[list[str]] = None, 519 skip: Optional[bool] = None, 520 skip_if: Optional[Callable[[Any], Any]] = None, 521 skip_if_false: Optional[bool] = None, 522 skip_if_default: Optional[bool] = None, 523 serializer: Optional[Callable[..., Any]] = None, 524 deserializer: Optional[Callable[..., Any]] = None, 525 flatten: Optional[Union[FlattenOpts, bool]] = None, 526 metadata: Optional[dict[str, Any]] = None, 527 **kwargs: Any, 528) -> Any: 529 """ 530 Declare a field with parameters. 531 """ 532 if not metadata: 533 metadata = {} 534 535 if rename is not None: 536 metadata["serde_rename"] = rename 537 if alias is not None: 538 metadata["serde_alias"] = alias 539 if skip is not None: 540 metadata["serde_skip"] = skip 541 if skip_if is not None: 542 metadata["serde_skip_if"] = skip_if 543 if skip_if_false is not None: 544 metadata["serde_skip_if_false"] = skip_if_false 545 if skip_if_default is not None: 546 metadata["serde_skip_if_default"] = skip_if_default 547 if serializer: 548 metadata["serde_serializer"] = serializer 549 if deserializer: 550 metadata["serde_deserializer"] = deserializer 551 if flatten is True: 552 metadata["serde_flatten"] = FlattenOpts() 553 elif flatten: 554 metadata["serde_flatten"] = flatten 555 556 return dataclasses.field(*args, metadata=metadata, **kwargs)
Declare a field with parameters.
125def default_deserializer(_cls: type[Any], obj: Any) -> Any: 126 """ 127 Marker function to tell serde to use the default deserializer. It's used when custom 128 deserializer is specified at the class but you want to override a field with the default 129 deserializer. 130 """
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.
442def asdict(v: Any) -> dict[Any, Any]: 443 """ 444 Serialize object into dictionary. 445 """ 446 return to_dict(v, reuse_instances=False, convert_sets=False)
Serialize object into dictionary.
399def astuple(v: Any) -> tuple[Any, ...]: 400 """ 401 Serialize object into tuple. 402 """ 403 return to_tuple(v, reuse_instances=False, convert_sets=False)
Serialize object into tuple.
95def default_serializer(_cls: type[Any], obj: Any) -> Any: 96 """ 97 Marker function to tell serde to use the default serializer. It's used when custom serializer 98 is specified at the class but you want to override a field with the default serializer. 99 """
Marker function to tell serde to use the default serializer. It's used when custom serializer is specified at the class but you want to override a field with the default serializer.
1000class ClassSerializer(Protocol): 1001 """ 1002 Interface for custom class serializer. 1003 1004 This protocol is intended to be used for custom class serializer. 1005 1006 >>> from datetime import datetime 1007 >>> from serde import serde 1008 >>> from plum import dispatch 1009 >>> class MySerializer(ClassSerializer): 1010 ... @dispatch 1011 ... def serialize(self, value: datetime) -> str: 1012 ... return value.strftime("%d/%m/%y") 1013 """ 1014 1015 def serialize(self, value: Any) -> Any: 1016 pass
Interface for custom class serializer.
This protocol is intended to be used for custom class serializer.
>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MySerializer(ClassSerializer):
... @dispatch
... def serialize(self, value: datetime) -> str:
... return value.strftime("%d/%m/%y")
1767def _no_init_or_replace_init(self, *args, **kwargs): 1768 cls = type(self) 1769 1770 if cls._is_protocol: 1771 raise TypeError('Protocols cannot be instantiated') 1772 1773 # Already using a custom `__init__`. No need to calculate correct 1774 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1775 if cls.__init__ is not _no_init_or_replace_init: 1776 return 1777 1778 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1779 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1780 # searches for a proper new `__init__` in the MRO. The new `__init__` 1781 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1782 # instantiation of the protocol subclass will thus use the new 1783 # `__init__` and no longer call `_no_init_or_replace_init`. 1784 for base in cls.__mro__: 1785 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1786 if init is not _no_init_or_replace_init: 1787 cls.__init__ = init 1788 break 1789 else: 1790 # should not happen 1791 cls.__init__ = object.__init__ 1792 1793 cls.__init__(self, *args, **kwargs)
1019class ClassDeserializer(Protocol): 1020 """ 1021 Interface for custom class deserializer. 1022 1023 This protocol is intended to be used for custom class deserializer. 1024 1025 >>> from datetime import datetime 1026 >>> from serde import serde 1027 >>> from plum import dispatch 1028 >>> class MyDeserializer(ClassDeserializer): 1029 ... @dispatch 1030 ... def deserialize(self, cls: type[datetime], value: Any) -> datetime: 1031 ... return datetime.strptime(value, "%d/%m/%y") 1032 """ 1033 1034 def deserialize(self, cls: Any, value: Any) -> Any: 1035 pass
Interface for custom class deserializer.
This protocol is intended to be used for custom class deserializer.
>>> from datetime import datetime
>>> from serde import serde
>>> from plum import dispatch
>>> class MyDeserializer(ClassDeserializer):
... @dispatch
... def deserialize(self, cls: type[datetime], value: Any) -> datetime:
... return datetime.strptime(value, "%d/%m/%y")
1767def _no_init_or_replace_init(self, *args, **kwargs): 1768 cls = type(self) 1769 1770 if cls._is_protocol: 1771 raise TypeError('Protocols cannot be instantiated') 1772 1773 # Already using a custom `__init__`. No need to calculate correct 1774 # `__init__` to call. This can lead to RecursionError. See bpo-45121. 1775 if cls.__init__ is not _no_init_or_replace_init: 1776 return 1777 1778 # Initially, `__init__` of a protocol subclass is set to `_no_init_or_replace_init`. 1779 # The first instantiation of the subclass will call `_no_init_or_replace_init` which 1780 # searches for a proper new `__init__` in the MRO. The new `__init__` 1781 # replaces the subclass' old `__init__` (ie `_no_init_or_replace_init`). Subsequent 1782 # instantiation of the protocol subclass will thus use the new 1783 # `__init__` and no longer call `_no_init_or_replace_init`. 1784 for base in cls.__mro__: 1785 init = base.__dict__.get('__init__', _no_init_or_replace_init) 1786 if init is not _no_init_or_replace_init: 1787 cls.__init__ = init 1788 break 1789 else: 1790 # should not happen 1791 cls.__init__ = object.__init__ 1792 1793 cls.__init__(self, *args, **kwargs)
1043def add_serializer(serializer: ClassSerializer) -> None: 1044 """ 1045 Register custom global serializer. 1046 """ 1047 GLOBAL_CLASS_SERIALIZER.append(serializer)
Register custom global serializer.
1050def add_deserializer(deserializer: ClassDeserializer) -> None: 1051 """ 1052 Register custom global deserializer. 1053 """ 1054 GLOBAL_CLASS_DESERIALIZER.append(deserializer)
Register custom global deserializer.