用于内置类型(float、int 等)的 pydantic `json_encoders`

pydantic `json_encoders` for builtin types (float, int, etc)

我在使用 pydantic 对 floatint 等字段进行 json 编码时遇到一些意外行为。这里是documentation对json的编码,供参考。

例如,这个模型似乎编码 complex 就好了,但忽略了我的 float 字段。

import pydantic as pd

class Model(pd.BaseModel):
    class Config:
        arbitrary_types_allowed=True
        json_encoders = {
            float: lambda x: 'test',
            complex: lambda x: 'test'
        }
    d1:float
    d2:complex

m = Model(d1=1.0, d2=1j)
m.json()
# '{"d1": 1.0, "d2": "test"}'

任何人都可以阐明这种行为并为我指明正确的方向吗?

我的用例是一个自定义编码器,用于检测 float 何时为 numpy.inf,然后将其写入 json 中的 "Infinity" 而不是非法默认值Infinityjson 包编码。

感谢您的帮助!

您的自定义 json_encoder 不适用于 float 类型的原因是 pydantic 使用 json.dumps() 进行序列化。 如果任何类型可以使用 json.dumps() 序列化,则不会对这些类型使用 cutom json_encoder。

然后来到 complex 类型,它不能被 json.dumps() 序列化,这就是它使用您提供的自定义 json_encoder 的原因。

如果您仍想使用自定义 json_encoder,您可以按照 pydantic 的建议使用 orjson

Custom JSON (de)serialisation

示例代码:

def orjson_dumps(v, *, default):
    for key, value in v.items():
        if isinstance(value, float):
            v[key] = 'test'
        elif isinstance(value, complex):
            v[key] = 'test'
    # orjson.dumps returns bytes, to match standard json.dumps we need to decode
    return orjson.dumps(v, default=default).decode()
    

class Model(BaseModel):
    class Config:
        arbitrary_types_allowed=True
        json_dumps = orjson_dumps
        json_loads = orjson.loads
    d1:float
    d2:complex

m = Model(d1=1.0, d2=1j)
m.json() # {"d1":"test","d2":"test"}