When to try to serialize a BaseModel dynamically created with create_model this error raises:
TypeError: Object of type ModelMetaclass is not JSON serializable
pydantic version: 1.7.3
python version: 3.8.6
ps: I found this when using a dynamically created BaseModel on the parameter response_model of FastAPI method.
A way to reproduce:
```py
In [1]: from pydantic import create_model
In [2]: import json
TypeError Traceback (most recent call last)
----> 1 json.dumps(create_model('BarModel', a='b', c='d'))
/usr/lib/python3.8/json/__init__.py in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw)
229 cls is None and indent is None and separators is None and
230 default is None and not sort_keys and not kw):
--> 231 return _default_encoder.encode(obj)
232 if cls is None:
233 cls = JSONEncoder
/usr/lib/python3.8/json/encoder.py in encode(self, o)
197 # exceptions aren't as detailed. The list call should be roughly
198 # equivalent to the PySequence_Fast that ''.join() would do.
--> 199 chunks = self.iterencode(o, _one_shot=True)
200 if not isinstance(chunks, (list, tuple)):
201 chunks = list(chunks)
/usr/lib/python3.8/json/encoder.py in iterencode(self, o, _one_shot)
255 self.key_separator, self.item_separator, self.sort_keys,
256 self.skipkeys, _one_shot)
--> 257 return _iterencode(o, 0)
258
259 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
/usr/lib/python3.8/json/encoder.py in default(self, o)
177
178 """
--> 179 raise TypeError(f'Object of type {o.__class__.__name__} '
180 f'is not JSON serializable')
181
TypeError: Object of type ModelMetaclass is not JSON serializable
In [4]:
...
Hi @berlotto
When you define a model, you need to use the json method on an instance (not the class!)
class BarModel(BaseModel):
a = 'b'
c = 'd'
print(BarModel().json())
# {"a": "b", "c": "d"}
So when you define you class dynamically, remember that create_model('BarModel', a='b', c='d') returns the class!
You hence need to do create_model('BarModel', a='b', c='d')().json()
Hope it helps!
I would like to add a case when an instance of a class is passed to an external library and this library uses json package to serialize data, then we can not force that library to use model's #json() method.
For example, I got a case when using celery, if I pass pydatic model Event to a celery task, error message like Object of type Event is not JSON serializable will appear.
Hi @trongbq and happy new year!
In this case it's beyond _pydantic_ scope. I also use celery for data pipelines and it's quite common to use a custom encoder for json for example.
In your case you could go with something like this
import json
from pydantic import BaseModel
class BarModel(BaseModel):
a = 'b'
c = 'd'
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, BaseModel):
return obj.dict()
return super().default(obj)
print(json.dumps(BarModel(), cls=CustomEncoder))
# {"a": "b", "c": "d"}
Hope it helps
Most helpful comment
Hi @trongbq and happy new year!
In this case it's beyond _pydantic_ scope. I also use celery for data pipelines and it's quite common to use a custom encoder for
jsonfor example.In your case you could go with something like this
Hope it helps