Yet another serialization library on top of dataclasses
—
Advanced field-level configuration for customizing serialization behavior, field renaming, conditional serialization, custom serializers, and field flattening. The field function provides fine-grained control over how individual fields are processed.
The field function extends dataclasses.field with serialization-specific parameters.
def field(
default=...,
default_factory=...,
init: bool = True,
repr: bool = True,
hash: bool | None = None,
compare: bool = True,
metadata: dict | None = None,
rename: str | None = None,
alias: list[str] | None = None,
serializer: Callable | None = None,
deserializer: Callable | None = None,
flatten: bool = False,
skip: bool = False,
skip_if: Callable | None = None,
skip_if_false: bool = False,
skip_if_default: bool = False,
) -> Any:
"""
Define field with serialization parameters.
Standard dataclass parameters:
- default: Default value for the field
- default_factory: Function to generate default value
- init: Include field in __init__
- repr: Include field in __repr__
- hash: Include field in __hash__
- compare: Include field in comparison operations
- metadata: Additional metadata dictionary
pyserde-specific parameters:
- rename: Alternative name for serialization
- alias: List of alternative names for deserialization
- serializer: Custom serializer function for this field
- deserializer: Custom deserializer function for this field
- flatten: Flatten nested object fields into parent
- skip: Skip field during serialization/deserialization
- skip_if: Function to conditionally skip field
- skip_if_false: Skip field if value is falsy
- skip_if_default: Skip field if value equals default
Returns:
Field descriptor with custom serialization behavior
"""Functions for registering global custom serialization logic.
def add_serializer(serializer: ClassSerializer) -> None:
"""
Register custom global serializer.
Parameters:
- serializer: ClassSerializer instance to register globally
"""
def add_deserializer(deserializer: ClassDeserializer) -> None:
"""
Register custom global deserializer.
Parameters:
- deserializer: ClassDeserializer instance to register globally
"""
def default_serializer(_cls: type[Any], obj: Any) -> Any:
"""
Marker function to tell serde to use the default serializer.
Used when custom serializer is specified at the class level but
you want to override a field with the default serializer.
Parameters:
- _cls: Class type (not used)
- obj: Object to serialize
Returns:
Serialized representation using default logic
"""
def default_deserializer(_cls: type[Any], obj: Any) -> Any:
"""
Marker function to tell serde to use the default deserializer.
Used when custom deserializer is specified at the class level but
you want to override a field with the default deserializer.
Parameters:
- _cls: Class type to deserialize to
- obj: Object to deserialize
Returns:
Deserialized object using default logic
"""Protocol classes for implementing custom class-level serialization behavior.
class ClassSerializer(Protocol):
"""Protocol for custom class serializers."""
def serialize(self, obj: Any) -> Any:
"""
Serialize object to intermediate representation.
Parameters:
- obj: Object to serialize
Returns:
Serialized representation
"""
class ClassDeserializer(Protocol):
"""Protocol for custom class deserializers."""
def deserialize(self, cls: type, data: Any) -> Any:
"""
Deserialize data to object instance.
Parameters:
- cls: Target class type
- data: Data to deserialize
Returns:
Deserialized object instance
"""from serde import serde, field
@serde
class User:
username: str = field(rename="user_name")
email_address: str = field(rename="email")
full_name: str = field(rename="name")
user = User("alice", "alice@example.com", "Alice Smith")
data = to_dict(user)
# {'user_name': 'alice', 'email': 'alice@example.com', 'name': 'Alice Smith'}from serde import serde, field
@serde
class Config:
host: str
port: int
debug: bool = field(skip_if_false=True)
secret_key: str | None = field(skip_if=lambda x: x is None)
default_timeout: int = field(default=30, skip_if_default=True)
config = Config("localhost", 8080, False, None, 30)
data = to_dict(config)
# {'host': 'localhost', 'port': 8080} # debug, secret_key, and default_timeout skippedfrom serde import serde, field
from datetime import datetime
def serialize_timestamp(dt: datetime) -> int:
return int(dt.timestamp())
def deserialize_timestamp(cls, timestamp: int) -> datetime:
return datetime.fromtimestamp(timestamp)
@serde
class Event:
name: str
created_at: datetime = field(
serializer=serialize_timestamp,
deserializer=deserialize_timestamp
)
event = Event("Meeting", datetime(2023, 12, 25, 10, 30))
data = to_dict(event)
# {'name': 'Meeting', 'created_at': 1703502600}from serde import serde, field
@serde
class Address:
street: str
city: str
country: str
@serde
class Person:
name: str
age: int
address: Address = field(flatten=True)
person = Person("Alice", 30, Address("123 Main St", "Boston", "USA"))
data = to_dict(person)
# {
# 'name': 'Alice',
# 'age': 30,
# 'street': '123 Main St',
# 'city': 'Boston',
# 'country': 'USA'
# }from serde import serde, add_serializer, add_deserializer
from decimal import Decimal
def decimal_serializer(obj: Decimal) -> str:
return str(obj)
def decimal_deserializer(cls, data: str) -> Decimal:
return Decimal(data)
# Register global serializers
add_serializer(Decimal, decimal_serializer)
add_deserializer(Decimal, decimal_deserializer)
@serde
class Product:
name: str
price: Decimal
product = Product("Widget", Decimal("19.99"))
data = to_dict(product)
# {'name': 'Widget', 'price': '19.99'}from serde import serde, ClassSerializer, ClassDeserializer
class TimestampSerializer:
def serialize(self, obj):
result = {}
for field_name, field_value in obj.__dict__.items():
if isinstance(field_value, datetime):
result[field_name] = int(field_value.timestamp())
else:
result[field_name] = field_value
return result
class TimestampDeserializer:
def deserialize(self, cls, data):
kwargs = {}
for field_name, field_value in data.items():
field_type = cls.__annotations__.get(field_name)
if field_type == datetime:
kwargs[field_name] = datetime.fromtimestamp(field_value)
else:
kwargs[field_name] = field_value
return cls(**kwargs)
@serde(
class_serializer=TimestampSerializer(),
class_deserializer=TimestampDeserializer()
)
class LogEntry:
message: str
timestamp: datetime
level: strInstall with Tessl CLI
npx tessl i tessl/pypi-pyserde