Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve JSON string from pydantic Json type

I have a pydantic model as follows.

from pydantic import Json, BaseModel
class Foo(BaseModel):
    id: int
    bar: Json

Foo.bar can parse JSON string as input and store it as a dict which is nice.

foo = Foo(id=1, bar='{"foo": 2, "bar": 3}')
type(foo.bar) #outputs dict

And if i want the entire object to be a dict I can do

foo.dict()
#outputs
{'id': 1, 'bar': {'foo': 2, 'bar': 3}}

But how can I export bar as JSON string as following

{'id': 1, 'bar': '{"foo": 2, "bar": 3}'}

I want to write JSON back into the database.

like image 715
Jey Avatar asked Aug 28 '26 14:08

Jey


2 Answers

Pydantic author here.

There's currently no way to do that without calling json.dumps(foo.bar). You could make that a method on Foo if you liked, which would be easier to use but require the same processing.

If performance is critical, or you need the exact same JSON string you started with (same spaces etc.) You could do one of the following:

  • Make bar a string field but add a validator to check it's valid JSON
  • create a custom data type to parse the JSON but also keep a reference to the raw JSON string
like image 145
SColvin Avatar answered Aug 30 '26 04:08

SColvin


I faced same problem so here comes a little hacky solution with class factory (some elegant one with something like foo: JsonVal[T] is almost impossible to implement due to numerous internal pydantic hacks like how it works with generic type annotations). Unfortunately type inference is not working, but validation and access to parsed value are ok, considering that Fields are always stored/serialized as str.


from abc import ABC
from typing import TypeVar, Generic, Type

from pydantic import Json, parse_obj_as, BaseModel

T = TypeVar('T')


class JsonVal(Generic[T], str, ABC):
    @property
    def parsed(self) -> T:
        return None


def json_val(t: Type) -> Type[JsonVal[T]]:
    class _JsonVal(JsonVal, str):
        _t: Type

        @classmethod
        def __get_validators__(cls):
            yield cls.validate

        @classmethod
        def validate(cls, v):
            parse_obj_as(Json[cls._t], v)
            return cls(v)

        @property
        def parsed(self):
            return parse_obj_as(Json[self._t], self)

    _JsonVal._t = t
    return _JsonVal


class Bar(BaseModel):
    bar: str


class Model(BaseModel):
    foo: json_val(list[int])
    bar: json_val(Bar)


m = Model.parse_obj({"foo": '[1,2,3]', "bar": '{"bar":"baz"}'})

print(m)
print(m.foo, type(m.foo), m.foo.parsed, type(m.foo.parsed))
print(m.bar, type(m.bar), m.bar.parsed, type(m.bar.parsed))
print(m.json())

# foo='[1,2,3]' bar='{"bar":"baz"}'
# f[1,2,3] <class '__main__.json_val.<locals>._JsonVal'> [1, 2, 3] <class 'list'>
# f{"bar":"baz"} <class '__main__.json_val.<locals>._JsonVal'> bar='baz' <class '__main__.Bar'>
# f{"foo": "[1,2,3]", "bar": "{\"bar\":\"baz\"}"}

like image 20
Anton Ovsyannikov Avatar answered Aug 30 '26 04:08

Anton Ovsyannikov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!