Skip to main content

Generic Mappings with DictSchema

In pydantic-core, generic mappings are handled by the DictSchema structure. Unlike TypedDictSchema, which defines a fixed set of keys with specific types, DictSchema is designed for dictionaries where any number of keys can exist, provided they and their corresponding values conform to specified sub-schemas.

The primary way to construct these schemas is through the dict_schema() helper function found in pydantic_core.core_schema.

Core Concepts

A DictSchema defines how to validate the keys and values of a dictionary. It is essentially a template for key-value pairs.

Key and Value Validation

The keys_schema and values_schema fields allow you to apply any CoreSchema to the dictionary's components. If omitted, they default to AnySchema.

from pydantic_core import SchemaValidator, core_schema

# A dictionary where keys must be strings and values must be integers
schema = core_schema.dict_schema(
keys_schema=core_schema.str_schema(),
values_schema=core_schema.int_schema()
)
v = SchemaValidator(schema)

# Validates and coerces values
assert v.validate_python({'a': '1', 'b': 2}) == {'a': 1, 'b': 2}

When validating JSON, keys are always strings. If keys_schema is something other than a string schema (e.g., int_schema), pydantic-core will attempt to coerce the string key into the target type.

Validation Constraints

DictSchema supports several constraints to control the size and validation flow of the mapping:

  • min_length and max_length: Restrict the number of items in the dictionary.
  • strict: If True, only actual Python dict objects are accepted. If False (the default), any object implementing the Mapping interface is accepted and coerced to a dict.
  • fail_fast: Determines whether validation should stop immediately upon encountering the first error.

Fail Fast Behavior

By default, pydantic-core collects all errors found during dictionary validation. Setting fail_fast=True changes this to stop at the first failure, which can be useful for performance or specific error reporting requirements.

# Example from pydantic-core/tests/validators/test_dict.py
v = SchemaValidator(
{
'type': 'dict',
'keys_schema': {'type': 'int'},
'values_schema': {'type': 'int'},
'fail_fast': True
}
)

# Validation stops at the first error ('a' is not an int)
# instead of continuing to check 'c'

Error Reporting and the [key] Marker

When a key fails validation, pydantic-core uses a specific convention in the error location to distinguish between a failure in the key itself versus a failure in the value associated with that key.

If a key fails validation, the location in the ValidationError will include the key's value followed by the special marker [key].

# Example from pydantic-core/tests/validators/test_dict.py
v = SchemaValidator(core_schema.dict_schema(keys_schema=core_schema.int_schema()))

try:
v.validate_python({'x': 1})
except ValidationError as exc:
# The error location points to the key 'x' specifically
assert exc.errors()[0]['loc'] == ('x', '[key]')

Serialization and Filtering

DictSchema also supports advanced serialization options through the serialization field. The filter_dict_schema() helper is used to define include or exclude sets for dictionary keys during serialization.

# Example from pydantic-core/tests/serializers/test_dict.py
from pydantic_core import SchemaSerializer, core_schema

schema = core_schema.dict_schema(
serialization=core_schema.filter_dict_schema(include={'a', 'c'})
)
s = SchemaSerializer(schema)

# Only keys 'a' and 'c' are included in the output
assert s.to_python({'a': 1, 'b': 2, 'c': 3, 'd': 4}) == {'a': 1, 'c': 3}

This filtering mechanism allows for fine-grained control over which data is exported, even when the underlying dictionary contains more information than needed for a specific output format.