Skip to content

encoder

Support for dynamically registered JSON encoders in pydantic.

Preparation:

For your top level BaseModel, set DynEncoderModelMeta as metaclass, e.g.

class MyBaseModel(BaseModel, metaclass=DynEncoderModelMeta):
    ...

(If you already use a custom metaclass for your base model, add the DynJsonEncoder metaclass mixin)

Usage:

Decorate any class with @json_encoder(ENCODER_FUNCTION).

To add an encoder for some existing class you cannot decorate, use add_json_encoder(ClassName, func).

Note that json_encoders declared as intended by Pydantic in the Config section will always be prioritized over the dynamic encoder. This means, that the dynamic encoders are only triggered for classes that are not models themselves (because pydantic handles them already).

Also note that to prevent bugs, you cannot override encoders for a class that already has a registered dynamic encoder. Use the normal pydantic mechanisms for cases where this is really needed.

Ideally, design your classes in a way that there is a 1-to-1 relationship between class and desired JSON encoder (e.g. you can have different subclasses with different encoders).

DynJsonEncoderMetaMixin

Bases: type

Metaclass mixin to first look in dynamic encoder registry.

Combine this with (a subclass of) ModelMetaClass and use it for your custom base model.

Source code in src/metador_core/schema/encoder.py
85
86
87
88
89
90
91
92
93
class DynJsonEncoderMetaMixin(type):
    """Metaclass mixin to first look in dynamic encoder registry.

    Combine this with (a subclass of) `ModelMetaClass` and use it for your custom base model.
    """

    def __init__(self, name, bases, dct):
        super().__init__(name, bases, dct)
        self.__json_encoder__ = staticmethod(_dynamize_encoder(self.__json_encoder__))

DynEncoderModelMetaclass

Bases: DynJsonEncoderMetaMixin, ModelMetaclass

Set this metaclass for your custom base model to enable dynamic encoders.

Source code in src/metador_core/schema/encoder.py
96
97
class DynEncoderModelMetaclass(DynJsonEncoderMetaMixin, ModelMetaclass):
    """Set this metaclass for your custom base model to enable dynamic encoders."""

json_encoder

json_encoder(func)

Decorate a class to register a new JSON encoder for it.

Source code in src/metador_core/schema/encoder.py
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def json_encoder(func):
    """Decorate a class to register a new JSON encoder for it."""

    def reg_encoder(cls):
        if issubclass(cls.__class__, ModelMetaclass):
            raise TypeError("This decorator does not work for pydantic models!")
        if hasattr(cls, "__dataclass_fields__"):
            raise TypeError("This decorator does not work for dataclasses!")

        if cls in _reg_json_encoders:
            raise ValueError(f"A JSON encoder function for {cls} already exists!")

        _reg_json_encoders[cls] = func
        return cls

    return reg_encoder

add_json_encoder

add_json_encoder(cls, func)

Register a JSON encoder function for a class.

Source code in src/metador_core/schema/encoder.py
62
63
64
def add_json_encoder(cls, func):
    """Register a JSON encoder function for a class."""
    return json_encoder(func)(cls)