Skip to main content

Serializing Temporal and Binary Types

To serialize temporal (datetime, date, time, timedelta) and binary (bytes) types, use the SchemaSerializer with specific CoreConfig options to control the output format in JSON mode.

Serializing Temporal Types

By default, temporal types are serialized to ISO8601 strings. You can change this behavior globally using the ser_json_temporal configuration option.

from datetime import datetime, timedelta
from pydantic_core import SchemaSerializer, core_schema

# Default ISO8601 serialization
v = SchemaSerializer(core_schema.datetime_schema())
assert v.to_python(datetime(2022, 12, 2, 12, 13, 14), mode='json') == '2022-12-02T12:13:14'
assert v.to_json(datetime(2022, 12, 2, 12, 13, 14)) == b'"2022-12-02T12:13:14"'

# Numeric serialization (seconds or milliseconds)
config = {'ser_json_temporal': 'seconds'}
v_num = SchemaSerializer(core_schema.datetime_schema(), config=config)
assert v_num.to_python(datetime(2022, 12, 2, 12, 13, 14), mode='json') == 1669983194

Customizing Timedelta Serialization

While ser_json_temporal affects all temporal types, you can use ser_json_timedelta to specifically target timedelta objects. Note that ser_json_temporal takes precedence if both are provided.

from datetime import timedelta
from pydantic_core import SchemaSerializer, core_schema

# Default Timedelta (ISO8601 duration)
v = SchemaSerializer(core_schema.timedelta_schema())
assert v.to_python(timedelta(days=2, hours=3), mode='json') == 'P2DT3H'

# Float seconds serialization
config = {'ser_json_timedelta': 'float'}
v_float = SchemaSerializer(core_schema.timedelta_schema(), config=config)
assert v_float.to_python(timedelta(seconds=4, microseconds=500_000), mode='json') == 4.5

Serializing Binary Types

Binary data (bytes) defaults to UTF-8 serialization. If your data contains non-UTF-8 bytes, you must configure an alternative encoding like base64 or hex to avoid errors.

from pydantic_core import SchemaSerializer, core_schema

# Base64 encoding
config_b64 = {'ser_json_bytes': 'base64'}
s_b64 = SchemaSerializer(core_schema.bytes_schema(), config=config_b64)
assert s_b64.to_json(b'foobar') == b'"Zm9vYmFy"'
assert s_b64.to_python(b'foobar', mode='json') == 'Zm9vYmFy'

# Hex encoding
config_hex = {'ser_json_bytes': 'hex'}
s_hex = SchemaSerializer(core_schema.bytes_schema(), config=config_hex)
assert s_hex.to_json(b'foo') == b'"666f6f"'

Temporal Types as Dictionary Keys

When temporal types are used as keys in a dictionary, they are always converted to strings in JSON mode, even if ser_json_temporal is set to a numeric type.

from datetime import date
from pydantic_core import SchemaSerializer, core_schema

schema = core_schema.dict_schema(core_schema.date_schema(), core_schema.int_schema())
v = SchemaSerializer(schema, config={'ser_json_temporal': 'seconds'})

# Even with 'seconds' config, the key is a string ISO8601 date
res = v.to_python({date(2022, 12, 2): 1}, mode='json')
assert res == {'2022-12-02': 1}

Common Gotchas

  • UTF-8 Errors: By default (ser_json_bytes: 'utf8'), serializing bytes that are not valid UTF-8 will raise a PydanticSerializationError. Use 'base64' or 'hex' for arbitrary binary data.
  • Configuration Precedence: The ser_json_temporal setting overrides ser_json_timedelta. If you want specific behavior for timedeltas while keeping other temporal types as ISO8601, ensure ser_json_temporal is not set.
  • Microseconds Precision: For TimeSchema, DatetimeSchema, and TimedeltaSchema, the microseconds_precision defaults to 'truncate'. You can set it to 'error' to fail if precision would be lost.
  • Dictionary Key Strings: In JSON mode, dictionary keys must be strings. Temporal keys will be converted to their string representation (ISO8601 or numeric string) regardless of the underlying value serialization format.