Skip to main content

JSON Serialization Customization

JSON serialization in this codebase is primarily controlled through the CoreConfig class defined in pydantic-core/python/pydantic_core/core_schema.py. This configuration allows you to define how complex Python types—specifically temporal objects, binary data, and special floating-point values—are represented when converted to JSON strings via the SchemaSerializer.

Temporal Types​

The serialization of datetime, date, time, and timedelta objects is managed by two main configuration keys: ser_json_temporal and ser_json_timedelta.

Global Temporal Control​

The ser_json_temporal setting is the primary switch for all date and time-related types. It supports three modes:

  • 'iso8601': (Default) Serializes objects to standard ISO 8601 strings (e.g., "2024-01-01T00:00:00").
  • 'seconds': Serializes objects to a Unix timestamp (float) representing seconds.
  • 'milliseconds': Serializes objects to a Unix timestamp (float) representing milliseconds.

When ser_json_temporal is set, it takes precedence over the more specific ser_json_timedelta setting.

from pydantic_core import SchemaSerializer, core_schema
from datetime import datetime

# Configuring serialization to milliseconds
s = SchemaSerializer(
core_schema.datetime_schema(),
config={'ser_json_temporal': 'milliseconds'}
)

dt = datetime(2024, 1, 1)
# Output is a float timestamp in milliseconds
assert s.to_json(dt) == b'1704067200000.0'

Timedelta Specifics​

The ser_json_timedelta setting specifically targets timedelta objects. It allows for:

  • 'iso8601': (Default) Serializes to ISO 8601 duration strings (e.g., "P1DT2H").
  • 'float': Serializes to a total number of seconds.

In the pydantic_core.core_schema.CoreConfig definition, this parameter is noted as being ignored if ser_json_temporal is also present, as the latter provides a unified format for all temporal types.

Binary Data​

The BytesSchema handles the validation and serialization of bytes objects. Because JSON does not have a native binary type, the ser_json_bytes configuration in CoreConfig determines the encoding strategy:

  • 'utf8': (Default) Attempts to decode bytes as a UTF-8 string. This will fail during serialization if the bytes contain non-UTF-8 compatible data.
  • 'base64': Encodes bytes into a Base64 string. This is the safest option for arbitrary binary data.
  • 'hex': Encodes bytes into a hexadecimal string.
from pydantic_core import SchemaSerializer, core_schema

# Using base64 for binary safety
s = SchemaSerializer(
core_schema.bytes_schema(),
config={'ser_json_bytes': 'base64'}
)

assert s.to_json(b'foobar') == b'"Zm9vYmFy"'

Complementing this is val_json_bytes, which defines how the serializer should expect bytes to be encoded when parsing JSON back into Python objects, ensuring symmetry between serialization and validation.

Special Float Values​

Standard JSON (RFC 8259) does not support Infinity, -Infinity, or NaN. The ser_json_inf_nan setting in CoreConfig provides three strategies for handling these values when they appear in float fields:

  • 'null': (Default) Converts inf and nan to the JSON null literal. This is the most compatible with standard JSON parsers.
  • 'strings': Converts values to their string representations: "Infinity", "-Infinity", or "NaN".
  • 'constants': Produces the bare literals Infinity, -Infinity, and NaN. Note that this results in invalid JSON according to the strict specification, though some parsers (like Python's json module) may accept it.
from pydantic_core import SchemaSerializer, core_schema

# Serializing special floats as strings for compatibility
s = SchemaSerializer(
core_schema.float_schema(),
config={'ser_json_inf_nan': 'strings'}
)

assert s.to_json(float('inf')) == b'"Infinity"'
assert s.to_json(float('nan')) == b'"NaN"'

These settings are applied globally to the SchemaSerializer instance and affect all relevant fields within the schema tree, from simple float fields to complex nested models.