Pydantic: Export fields of the same type with different encodings

Question:

Suppose I have a model with various timedelta fields, but I want them each expressed in a different format when exported to JSON. I know I can specify the JSON encoder for timedelta, but that applies to all fields of that type. Is there a way to specify the JSON encoder for a given field? or is there another way to accomplish this?

Here’s a bit of code as an example:

from datetime import datetime, timedelta
from pydantic import BaseModel
from pydantic.json import timedelta_isoformat


class Example(BaseModel):
    delta_iso: timedelta # export using timedelta_isoformat
    delta_seconds_int: timedelta # export as int in seconds
    delta_seconds_float: timedelta # export as float in seconds
    delta_milliseconds_int: timedelta # export as int in milliseconds

    class Config:
        # This won't work because it applies to all fields above
        json_encoders = {
            timedelta: timedelta_isoformat,
        }
Asked By: aiguofer

||

Answers:

You can provide custom json_dumps function. Like so:

import json
from datetime import datetime, timedelta
from pydantic import BaseModel
from pydantic.json import timedelta_isoformat


def custom_dumps(v, *, default):
    for key, value in v.items():
        if key == "delta_iso":
            v[key] = timedelta_isoformat(value)
        elif key == "delta_seconds_int":
            v[key] = int(value.total_seconds())
        elif key == "delta_seconds_float":
            v[key] = value.total_seconds()
        elif key == "delta_milliseconds_int":
            v[key] = value.total_seconds() * 1000
    return json.dumps(v, default=default)


class Example(BaseModel):
    delta_iso: timedelta # export using timedelta_isoformat
    delta_seconds_int: timedelta # export as int in seconds
    delta_seconds_float: timedelta # export as float in seconds
    delta_milliseconds_int: timedelta # export as int in milliseconds

    class Config:
        json_dumps = custom_dumps

diff = datetime.timedelta(milliseconds=5000)
print(Example(delta_iso=diff, delta_seconds_int=diff, delta_seconds_float=diff, delta_milliseconds_int=diff).json())
{"delta_iso": "P0DT0H0M5.000000S", "delta_seconds_int": 5, "delta_seconds_float": 5.0, "delta_milliseconds_int": 5000.0}
Answered By: alex_noname

Another option, model_dump(mode="...") (which currently includes json and python modes) is eventually making its way into Pydantic, as discussed by the library’s author in Pydantic’s issue #1409:

This is actually already implemented in pydantic-core and works on main now.

Example:

from datetime import datetime
from pydantic import BaseModel


class MyModel(BaseModel):
    things: set[str]
    when: datetime


m = MyModel(things={'a', 'b'}, when=datetime.now())
print(m.model_dump(mode='json'))
"""
{
    'things': ['b', 'a'],
    'when': '2023-02-09T10:54:50.11279',
}
"""

As of today (March 9, 2023) it has not been packaged as a release, but it’s in the main branch as of f4e8f71ab23a and you can always try it out in the meantime by pinning your pydantic package to the appropriate main revision on GitHub.

Answered By: Jeffrey Wilges
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.