41

I'd like to use pydantic for handling data (bidirectionally) between an api and datastore due to it's nice support for several types I care about that are not natively json-serializable. It has better read/validation support than the current approach, but I also need to create json-serializable dict objects to write out.

from uuid import UUID, uuid4
from pydantic import BaseModel

class Model(BaseModel):
    the_id: UUID

instance = Model(the_id=uuid4())
print("1: %s" % instance.dict()
print("2: %s" % instance.json()

prints

{'the_id': UUID('4108356a-556e-484b-9447-07b56a664763')}
>>> inst.json()
'{"the_id": "4108356a-556e-484b-9447-07b56a664763"}'

Id like the following:

{"the_id": "4108356a-556e-484b-9447-07b56a664763"} # eg "json-compatible" dict

It appears that while pydantic has all the mappings, but I can't find any usage of the serialization outside the standard json ~recursive encoder (json.dumps( ... default=pydantic_encoder)) in pydantic/main.py. but I'd prefer to keep to one library for both validate raw->obj (pydantic is great at this) as well as the obj->raw(dict) so that I don't have to manage multiple serialization mappings. I suppose I could implement something similar to the json usage of the encoder, but this should be a common use case?

Other approaches such as dataclasses(builtin) + libraries such as dataclasses_jsonschema provide this ~serialization to json-ready dict, but again, hoping to use pydantic for the more robust input validation while keeping things symmetrical.

Jason Desrosiers
  • 22,479
  • 5
  • 47
  • 53
some bits flipped
  • 2,592
  • 4
  • 27
  • 42
  • See my reply at https://stackoverflow.com/a/69740271/317460 - It's a Pydantic custom type, specific for the example of UUID – RaamEE Oct 27 '21 at 14:15

4 Answers4

30

The current version of pydantic does not support creating jsonable dict straightforwardly. But you can use the following trick:

Note: This is a suboptimal solution

class Model(BaseModel):
    the_id: UUID = Field(default_factory=uuid4)

print(json.loads(Model().json()))
{'the_id': '4c94e7bc-78fe-48ea-8c3b-83c180437774'}

Or more efficiently by means of orjson

orjson.loads(Model().json())
alex_noname
  • 26,459
  • 5
  • 69
  • 86
  • it looks like @samuelcolvin is considering adding a `simplify` arg to `dict()` or similar https://github.com/samuelcolvin/pydantic/issues/951#issuecomment-552463606; as this was proposed by the pydantic author, I'm hoping there is progress, but haven't finished digging through github yet – some bits flipped Mar 27 '21 at 02:32
  • 7
    I appreciate your answer, but paying for a full round trip of serialization/deserialization can't be considered for a production environment. For the moment we've implemented a custom workaround. – some bits flipped Mar 27 '21 at 02:35
  • I wouldn't call it trick. It's rather something even more ugly than `a = True\n if len(str(a)) == 4: ..." – Stepan Dyatkovskiy Sep 15 '22 at 16:35
14

it appears this functionality has been proposed, and (may be) favored by pydantic's author samuel colvin, as https://github.com/samuelcolvin/pydantic/issues/951#issuecomment-552463606

which proposes adding a simplify parameter to Model.dict() to output jsonalbe data.

This code runs in a production api layer, and is exersized such that we can't use the one-line workaround suggested (just doing a full serialize (.json()) + full deserialize). We implemented a custom function to do this, descending the result of .dict() and converting types to jsonable - hopefully the above proposed functionality is added to pydantic in the future.

some bits flipped
  • 2,592
  • 4
  • 27
  • 42
  • 1
    This has landed since then: https://pydantic-docs.helpmanual.io/usage/exporting_models/#modeldict – luckydonald Feb 08 '22 at 13:20
  • 7
    What exactly has been added? Unfortunately, no functionality to add `dict_encoders` like `json_encoders` has been added so far. – Yury V. Zaytsev Apr 16 '22 at 20:44
  • @luckydonald I'm missing where in that doc (as of this writing) there's any mention of anything like this `simplify` parameter to `Model.dict()`. – mikenerone Oct 13 '22 at 21:03
  • This proposal seems to have been scrapped. They went with [this method](https://stackoverflow.com/a/75739762/2011147) instead. – Selcuk Mar 15 '23 at 01:25
12

Another alternative is to use the jsonable_encoder method from fastapi if you're using that already: https://fastapi.tiangolo.com/tutorial/encoder/

The code seems pretty self-contained so you could copy paste it if the license allows it.

aiguofer
  • 1,887
  • 20
  • 34
6

The official method in Pydantic 2 (which is not released as of this answer) is using the .model_dump() method with mode="json" argument:

print(instance.model_dump(mode="json"))

From the Pydantic 2 Plan document:

def model_dump_json(self, ...) -> str:
   """
   previously `json()`, arguments as above
   effectively equivalent to `json.dump(self.model_dump(..., mode='json'))`,
   but more performant
   """
Selcuk
  • 57,004
  • 12
  • 102
  • 110