如何为Pydantic自定义编码器传递JSON_OPTIONS优化MongoDB日期序列化?
你用错了Pydantic的json()方法的encoder参数——这个参数接受的是JSONEncoder类,不是直接传json_util.dumps这种序列化函数。你传json_util.dumps的时候,Pydantic内部会尝试把它当作类去实例化,而且后面的json_options参数会被传给默认的JSONEncoder构造函数,自然就报“意外参数”的错了。
方案1:直接转字典后用PyMongo插入(最优)
MongoDB的驱动PyMongo本身就支持直接处理Python的datetime和ObjectId类型,根本不需要先转成JSON字符串。你只需要把Pydantic对象转成Python字典,直接丢给insert_one或者insert_many就行:
from bson import ObjectId from datetime import datetime import pydantic import pymongo class A(pydantic.BaseModel): w: ObjectId x: datetime # 初始化MongoDB客户端 client = pymongo.MongoClient("mongodb://localhost:27017/") db = client["test_db"] collection = db["test_col"] # 实例化对象并转字典插入 a = A(w=ObjectId("650000000000000000000000"), x=datetime(2023,1,1)) collection.insert_one(a.model_dump()) # 旧版Pydantic用a.dict()
这样插入后,MongoDB里的x会是标准的ISODate类型,w也会保持ObjectId类型,完全符合预期。
方案2:如果确实需要用json_util控制序列化格式
要是你非得生成特定格式的BSON/JSON字符串,别用Pydantic的json()方法,先转字典再直接调用json_util.dumps,同时传入json_options:
from bson import ObjectId, json_util from bson.json_util import DatetimeRepresentation from datetime import datetime import pydantic class A(pydantic.BaseModel): w: ObjectId x: datetime a = A(w=ObjectId("650000000000000000000000"), x=datetime(2023,1,1)) # 配置日期为ISO8601格式 json_options = json_util.JSONOptions(datetime_representation=DatetimeRepresentation.ISO8601) # 直接用json_util序列化字典 serialized = json_util.dumps(a.model_dump(), json_options=json_options) print(serialized) # 输出示例:{"w": {"$oid": "650000000000000000000000"}, "x": {"$date": "2023-01-01T00:00:00Z"}}
要是想把这个字符串转回可插入MongoDB的对象,用json_util.loads()处理后再插入即可。
补充说明
Pydantic的BaseModel.json()方法的encoder参数,设计目的是传入自定义的JSONEncoder子类(比如继承自json.JSONEncoder的类),而不是直接传序列化函数。你之前的用法不符合参数要求,所以json_options根本传不到json_util.dumps里,才会出现报错。
内容的提问来源于stack exchange,提问作者tubular

