Reputation: 2016
I am having below pydantic models.
class SubModel(BaseModel):
columns: Mapping
key: List[str]
required: Optional[List[str]]
class Config:
anystr_strip_whitespace: True
extra: Extra.allow
allow_population_by_field_name: True
class MyModel(BaseModel):
name: str
config1: Optional[SubModel]
config2: Optional[Mapping]
class Config:
anystr_strip_whitespace: True
extra: Extra.allow
allow_population_by_field_name: True
When I am trying to do a dumps
on this, I am getting model is not JSON serializable
from io import BytesIO
from orjson import dumps
bucket = s3.Bucket(bucket_name)
bucket.upload(BytesIO(dumps(data)), key, ExtraArgs={'ContentType': 'application/json'})
Error -
TypeError: Type is not JSON serializable: MyModel
data
is a normal python dictionary with one of item of type MyModel
. Tried to use .json()
but get dict has no attribute json
.
I am stuck here. Can someone help me.
Upvotes: 30
Views: 60091
Reputation: 7121
With Pydantic v2 and FastAPI / Starlette you can create a less picky JSONResponse
using Pydantic's model.model_dump_json(...)
(doc) by overriding JSONResponse.render()
(starlette doc)
Pydantic can serialize many commonly used types to JSON that would otherwise be incompatible with a simple json.dumps(foobar) (e.g. datetime, date or UUID).
Also NaN, btw.
from fastapi.responses import JSONResponse
from pydantic import BaseModel
class JSONResponsePydanticV2(JSONResponse):
"""JSON response for Pydantic models."""
def render(self, content: BaseModel) -> bytes:
"""Render JSON response."""
if content is None:
return b""
if isinstance(content, list) and len(content) == 0:
return b"[]"
if isinstance(content, dict) and len(content) == 0:
return b"{}"
return content.model_dump_json(exclude_unset=True, exclude_none=True).encode(
"utf-8"
)
I think this is basically something like FastAPI's jsonable_encoder()
plus json.dumps()
.
Upvotes: 1
Reputation: 583
Use the following instructions:
from fastapi.responses import JSONResponse
from fastapi.encoders import jsonable_encoder
return JSONResponse(jsonable_encoder(pydantic models))
Upvotes: 0
Reputation: 792
Got similar issue for FastAPI
response, solved by:
return JSONResponse(content=jsonable_encoder(item), status_code=200)
or can be just like this:
return jsonable_encoder(item)
where jsonable_encoder
is:
from fastapi.encoders import jsonable_encoder
More details are here: https://fastapi.tiangolo.com/tutorial/encoder/
Upvotes: 27
Reputation: 39
In modern Pydantic, there is a method json()
b = Book(name="King")
return b.json()
In general, I could recommend to run dir(b), and then you can see all the methods, properties, fields, and so on
Upvotes: 3
Reputation: 91
From my perspective, the best way is to use built-in Pydantic model method model_dump_json()
Like:
b = Book(name="King")
return web.json_response(b.model_dump_json())
Upvotes: 8
Reputation: 3417
What about just setting a default encoder (which is used if all else fails)?
orjson.dumps(
MyModel(name="asd"),
default=lambda x: x.dict()
)
# Output: b'{"name":"asd","config1":null,"config2":null}'
Or with more nested:
orjson.dumps(
{"a_key": MyModel(name="asd")},
default=lambda x: x.dict()
)
# Output: b'{"a_key":{"name":"asd","config1":null,"config2":null}}'
If you have other types than Pydantic, just make a function and handle all types you have separately.
It also works with json library from standard library (for those that don't use orjson).
Upvotes: 2
Reputation: 186
Here the problem is that pydantic models are not json serializable by default, in your case, you can call data.dict() to serialize a dict version of your model.
from io import BytesIO
from orjson import dumps
bucket = s3.Bucket(bucket_name)
bucket.upload(BytesIO(dumps(data.dict())), key, ExtraArgs={'ContentType': 'application/json'})
Upvotes: 1