Marshmallow Union
marshmallow-union provides a `Union` field for Marshmallow schemas, allowing a single field to accept and serialize/deserialize values that conform to one of several specified field types. The library works by trying a list of fields one by one until one succeeds. The current version is 0.1.15.post1, released in June 2020, indicating a maintenance or inactive release cadence.
Warnings
- gotcha The `Union` field deserializes/serializes by trying fields in the order they are provided until one successfully processes the value without raising an error. This can lead to unexpected behavior if an earlier field accepts an unintended type (e.g., `fields.Integer()` might accept a string like '123'). For precise control over type matching, consider using `marshmallow-polyfield` instead.
- breaking Starting from version 0.1.12, if all candidate fields within a `Union` fail during serialization, a `marshmallow_union.ExceptionGroup` is raised. Prior versions might have behaved differently, possibly returning `None` or an empty dictionary.
- gotcha When using `Union` with `Nested` schemas, the serialization process still follows the 'first successful match' logic. If the first nested schema in the `Union` successfully processes *some* part of the input (even if not fully matching the intended type), subsequent nested schemas for other types will not be attempted, potentially leading to incomplete or incorrect dumps (e.g., an empty dictionary for the nested field).
- gotcha The library has not been updated since June 2020. This means it may have compatibility issues with newer versions of Marshmallow (e.g., Marshmallow 4.x, released in April 2026), which introduced significant breaking changes in field usage, validation, and serialization behavior.
Install
-
pip install marshmallow-union
Imports
- Union
from marshmallow_union import Union
Quickstart
import marshmallow
from marshmallow import Schema, fields, ValidationError
from marshmallow_union import Union
class MySchema(Schema):
id = fields.Integer(required=True)
value = Union(fields=[fields.Integer(), fields.String()], required=True)
# Deserialization (loading)
data_int = {'id': 1, 'value': 123}
data_str = {'id': 2, 'value': 'hello'}
data_invalid = {'id': 3, 'value': []}
schema = MySchema()
# Load an integer value
loaded_int = schema.load(data_int)
print(f"Loaded Int: {loaded_int}")
assert loaded_int == {'id': 1, 'value': 123}
# Load a string value
loaded_str = schema.load(data_str)
print(f"Loaded String: {loaded_str}")
assert loaded_str == {'id': 2, 'value': 'hello'}
# Attempt to load an invalid value
try:
schema.load(data_invalid)
except ValidationError as e:
print(f"Validation Error: {e.messages}")
assert 'value' in e.messages
# Serialization (dumping)
dumped_int = schema.dump(loaded_int)
print(f"Dumped Int: {dumped_int}")
assert dumped_int == data_int
dumped_str = schema.dump(loaded_str)
print(f"Dumped String: {dumped_str}")
assert dumped_str == data_str