Field Attributes¶
Field attributes are options to customize (de)serialization behaviour for a field of a dataclass. Use them when you want different wire formats, optional behavior, or custom logic for specific fields.
Attributes offered by dataclasses¶
default / default_factory¶
default and default_factory work as expected. If a field has default or default_factory attribute, it behaves like an optional field. If the field is found in the data, the value is fetched from the data and set in the deserialized object. If the field is not found in the data, the specified default value is set in the deserialized object.
class Foo:
a: int = 10
b: int = field(default=10) # same as field "a"
c: dict[str, int] = field(default_factory=dict)
print(from_dict(Foo, {})) # prints Foo(a=10, b=10, c={})
See examples/default.py for the complete example.
ClassVar¶
dataclasses.ClassVar is a class variable for the dataclasses. Since dataclass treats ClassVar as pseudo-field and dataclasses.field doesn't pick ClassVar, pyserde doesn't (de)serialize ClassVar fields as a default behaviour. If you want to serialize ClassVar fields, consider using serialize_class_var class attribute.
See examples/class_var.py for the complete example.
Attributes offered by pyserde¶
Field attributes can be specified through serde.field or dataclasses.field. We recommend to use serde.field because it's shorter and type check works.
Here is an example specifying rename attribute in both serde.field and dataclasses.field.
@serde.serde
class Foo:
a: str = serde.field(rename="A")
b: str = dataclasses.field(metadata={"serde_rename": "B"})
rename¶
rename is used to rename field name during (de)serialization. This attribute is convenient when you want to use a python keyword in field name. For example, this code renames field name id to ID.
See examples/rename.py for the complete example.
skip¶
skip is used to skip (de)serialization of the field with this attribute.
@serde
class Resource:
name: str
hash: str
metadata: dict[str, str] = field(default_factory=dict, skip=True)
See examples/skip.py and examples/skip_if_default_class.py for complete examples.
skip_serializing¶
skip_serializing omits the field only when serializing. The field is still accepted on input.
See examples/skip_serializing_deserializing.py for the complete example.
skip_deserializing¶
skip_deserializing ignores incoming data for the field and keeps its default/default_factory value. A default is required when the field participates in __init__.
@serde
class Profile:
username: str
session_id: str = field(default="generated", skip_deserializing=True)
See examples/skip_serializing_deserializing.py for the complete example.
skip_if¶
skip_if is used to skip (de)serialization of the field if the predicate function returns True.
See Class Attributes: skip_if_default for the class-level toggle, and examples/skip.py plus examples/skip_if_default_class.py for the complete examples.
Note
skip, skip_if, skip_if_false, skip_if_none, and skip_if_default apply to both serialization and deserialization. Use skip_serializing / skip_deserializing to make direction-specific choices.
skip_if_false¶
skip is used to skip (de)serialization of the field if the field evaluates to False. For example, this code skip (de)serializing if enemies is empty.
See examples/skip.py for the complete example.
skip_if_none¶
skip_if_none skips (de)serialization of the field when its value is None.
You can also enable this for all fields in a class; see Class Attributes: skip_if_none. See examples/skip_if_none.py for a runnable example.
skip_if_default¶
skip_if_default skips (de)serialization of the field when its value equals the default.
You can also enable it for every field in a class at once:
Field-level values still override the class setting; set skip_if_default=False on a field to keep it even when class-level skipping is enabled.
See examples/skip.py for the complete example.
alias¶
You can set aliases for field names. Alias only works for deserialization.
Foo can be deserialized from either {"a": "..."}, {"b": "..."} or {"c": "..."}.
See examples/alias.py for complete example.
serializer/deserializer¶
Sometimes you want to customize (de)serializer for a particular field, such as * You want to serialize datetime into a different format * You want to serialize a type in a third party package
In the following example, field a is serialized into "2021-01-01T00:00:00" by the default serializer for datetime, whereas field b is serialized into "01/01/21" by the custom serializer.
@serde
class Foo:
a: datetime
b: datetime = field(serializer=lambda x: x.strftime('%d/%m/%y'), deserializer=lambda x: datetime.strptime(x, '%d/%m/%y'))
See examples/custom_field_serializer.py for the complete example.
flatten¶
The flatten attribute can be used in two ways:
Flatten nested dataclass¶
You can flatten the fields of the nested dataclass structure.
Bar's c, d fields are deserialized as if they are defined in Foo. So you will get {"a":10,"b":"foo","c":100.0,"d":true} if you serialize Foo into JSON.
See examples/flatten.py for complete example.
Capture additional fields with dict¶
You can use dict[str, Any] or bare dict with flatten=True to capture all unknown/extra fields during deserialization, similar to Rust serde's #[serde(flatten)] on HashMap<String, Value>.
@serde
class User:
id: int
name: str
extra: dict[str, Any] = field(flatten=True, default_factory=dict)
# Deserialization - extra fields captured
user = from_json(User, '{"id": 1, "name": "Alice", "role": "admin", "active": true}')
# user.extra == {"role": "admin", "active": True}
# Serialization - extra fields merged back
user2 = User(id=2, name="Bob", extra={"department": "Engineering"})
to_json(user2) # '{"id": 2, "name": "Bob", "department": "Engineering"}'
Key features: - During deserialization, any fields not matching declared fields are captured in the dict - During serialization, the dict contents are merged into the output (declared fields take precedence) - Works with all data formats (JSON, YAML, TOML, MessagePack, etc.) - Can be combined with flattened dataclass fields
See examples/flatten_dict.py for complete example.