Colander
Colander is a Python library providing a simple schema-based serialization and deserialization framework, currently at version 2.0. It allows developers to define data schemas to validate and transform data structures (like those from XML, JSON, or HTML forms) into Python objects, and vice-versa. The project is actively maintained, with a focus on stability and compatibility with modern Python versions, and typically releases updates as needed.
Common errors
-
TypeError: sequence item 1: expected str instance, NoneType found
cause This error typically occurs when `colander.Invalid.asdict()` is called and one of the validation messages (`Invalid.msg`) in the error tree is `None` or a list of strings, which older versions of Colander's `colander.All` validator did not handle gracefully.fixUpgrade to Colander 2.0 or later. If using custom validators with `colander.All` in older versions, ensure they consistently return string messages for `Invalid.msg`. -
colander.Invalid: {'field_name': 'Error message for field'}cause This is the standard exception raised by Colander when input data fails to meet the requirements defined by the schema (e.g., wrong data type, missing a required field, value outside a specified range, or failing a custom validator).fixCatch the `colander.Invalid` exception and use its `asdict()` method to get a dictionary of specific error messages. Adjust the input data to conform to the schema's rules based on these messages. -
Output contains 'b'' prefix (e.g., b'my_value') when serializing strings.
cause In Colander 2.0, when a `bytes` object is provided to a `colander.String` schema node with `encoding` specified, it's passed through Python's `str()` function before processing, which adds the `b''` prefix if the object is still `bytes`.fixBefore passing `bytes` data to a `colander.String` schema node for serialization, explicitly decode it to a Python `str` (e.g., `my_bytes_value.decode('utf-8')`).
Warnings
- breaking Colander 2.0 dropped support for older Python versions (2.7, 3.4, 3.5, 3.6). Users on these versions must upgrade their Python environment or remain on Colander 1.x.
- breaking When serializing a `bytes` object via a `colander.String` schema node with the `encoding` parameter specified in Colander 2.0, the `bytes` object is now passed directly through `str()` before encoding. This results in an output string with a `b''` prefix (e.g., `b'my_string'`).
- gotcha In Colander versions prior to 0.9.1, unpickling `colander.null` would create a new instance of `_null` instead of returning the singleton, causing `is colander.null` checks to fail across pickling boundaries.
- gotcha Older versions of Colander's `colander.All` validator could cause `colander.Invalid.asdict()` to crash with a `TypeError` if `Invalid.msg` was `None` or a list. This was fixed in version 2.0.
- gotcha An issue existed in older `Mapping` and `Sequence` schemas where a `default` value of `colander.drop` incorrectly caused missing values to be dropped during deserialization. `colander.drop` should only affect serialization of default values, and only `missing` should affect deserialization. This was fixed in 2.0.
Install
-
pip install colander
Imports
- SchemaNode
from colander import SchemaNode
- MappingSchema
from colander import MappingSchema
- SequenceSchema
from colander import SequenceSchema
- String
from colander import String
- Int
from colander import Int
- Float
from colander import Float
- Boolean
from colander import Boolean
- Range
from colander.validators import Range
from colander import Range
- Invalid
from colander.exceptions import Invalid
from colander import Invalid
- null
from colander import null
Quickstart
import colander
class UserSchema(colander.MappingSchema):
name = colander.SchemaNode(colander.String())
age = colander.SchemaNode(colander.Int(), validator=colander.Range(min=0, max=150))
email = colander.SchemaNode(colander.String(), validator=colander.Email(), missing=colander.drop)
# --- Deserialization (Input Validation) ---
# Valid data
appstruct = {'name': 'Alice', 'age': 30, 'email': 'alice@example.com'}
schema = UserSchema()
try:
deserialized_data = schema.deserialize(appstruct)
print(f"Successfully deserialized: {deserialized_data}")
except colander.Invalid as e:
print(f"Deserialization failed: {e.asdict()}")
# Invalid data (age out of range, missing required name, invalid email)
appstruct_invalid = {'name': 'Bob', 'age': 200, 'email': 'invalid-email'}
try:
schema.deserialize(appstruct_invalid)
except colander.Invalid as e:
print(f"Deserialization failed with errors: {e.asdict()}")
# Data with 'missing=colander.drop' field omitted
appstruct_partial = {'name': 'Charlie', 'age': 25}
try:
deserialized_partial = schema.deserialize(appstruct_partial)
print(f"Deserialized partial data: {deserialized_partial}")
except colander.Invalid as e:
print(f"Deserialization of partial data failed: {e.asdict()}")
# --- Serialization (Output Generation) ---
# Python application structure
python_data = {'name': 'Dave', 'age': 40}
serialized_data = schema.serialize(python_data)
print(f"Successfully serialized: {serialized_data}")
python_data_full = {'name': 'Eve', 'age': 22, 'email': 'eve@example.com'}
serialized_data_full = schema.serialize(python_data_full)
print(f"Successfully serialized full data: {serialized_data_full}")