Pydantic: json编码的字典键



当使用(哈希)对象作为字典键时,调用.json()失败,因为当值被编码时,键没有:

from pydantic import BaseModel
from typing import dict
from datetime import datetime
class Foo(BaseModel): 
date: datetime 
sdict: Dict[datetime, str]
class Config:
json_encoders = {
datetime: repr
}
foo = Foo(date=datetime.now(), sdict={datetime.now(): 'now'})
foo                                                                                                                                                                                                      
# Foo(date=datetime.datetime(2021, 9, 3, 12, 9, 55, 36105), sdict={datetime.datetime(2021, 9, 3, 12, 9, 55, 36114): 'now'})
foo.json()
TypeError: keys must be a string
# to prove the other way around works:
class Foo(BaseModel): 
date: datetime 
sdict: Dict[str, datetime]
class Config:
json_encoders = {
datetime: repr
}
foo = Foo(date=datetime.now(), sdict={'now': datetime.now()})
foo.json()                                                                                                                                                                                               
# '{"date": "datetime.datetime(2021, 9, 3, 12, 13, 30, 606880)", "sdict": {"now": "datetime.datetime(2021, 9, 3, 12, 13, 30, 606884)"}}'

这是因为最终用于转储的json.dumps()中的default=参数不编码字典键。定义JSON编码器类确实有效,但由于其他原因,它不适合我。

我在pydantic中见过TypedDict,但它似乎没有解决这个问题。实际上,我不确定TypedDict的用途是什么,因为您需要定义字典中的每个键,这使得它类似于静态对象?

我的用例是我需要表示以下想法:

{
"report": {
"warehouses": {
warehouse.id: {
"name": warehouse.name,
"address": warehouse.address,
}
for warehouse in warehouses
}
}

andwarehouse.id是一个Identifier对象,可以根据需要转换为不同的格式,json编码器将其转换为字符串。

有谁知道除了字典之外的一种方式,我可以将任意键添加到对象中,这种方式将受到json编码器或其他序列化方式的影响?

解决这个问题的一个选项是为pydantic模型使用自定义json_dumps函数,在其中进行自定义序列化,我通过继承JSONEncoder来实现。

例如:

import json
from pydantic import BaseModel
from typing import Dict
from datetime import datetime

class CustomEncoder(json.JSONEncoder):
def _transform(self, v):
res = v
if isinstance(v, datetime):
res = v.isoformat()
# else other variants
return self._encode(res)
def _encode(self, obj):
if isinstance(obj, dict):
return {self._transform(k): self._transform(v) for k, v in obj.items()}
else:
return obj
def encode(self, obj):
return super(CustomEncoder, self).encode(self._encode(obj))

def custom_dumps(values, *, default):
return CustomEncoder().encode(values)

class Foo(BaseModel):
date: datetime
sdict: Dict[datetime, str]
class Config:
json_dumps = custom_dumps

foo = Foo(date=datetime.now(), sdict={datetime.now(): 'now'})
Foo(date=datetime(2021, 9, 3, 12, 9, 55, 36105), sdict={datetime(2021, 9, 3, 12, 9, 55, 36114): 'now'})
print(foo.json())
{"date": "2021-09-07T16:02:51.070159", "sdict": {"2021-09-07T16:02:51.070164": "now"}}

相关内容

  • 没有找到相关文章

最新更新