pydantic
With pydantic models you get a dataclasses-like experience and full support for dict and Json conversions (and much more).
Python 3.9 and above:
from typing import Optional
from pydantic import BaseModel, parse_obj_as, parse_raw_as
class Foo(BaseModel):
count: int
size: Optional[float] = None
f1 = Foo(count=10)
# Parse to dict
print(f1.dict())
# OUT: {'count': 10, 'size': None}
# Load from dict
f2 = Foo.parse_obj({"count": 20})
# Parse to json
print(f2.json())
# OUT: {"count": 20, "size": null}
More options:
# Load from json string
f3 = Foo.parse_raw('{"count": 30}')
# Load from json file
f4 = Foo.parse_file("path/to/data.json")
# Load from list of dicts
f_list1 = parse_obj_as(list[Foo], [{"count": 110}, {"count": 120}])
print(f_list1)
# OUT: [Foo(count=110, size=None), Foo(count=120, size=None)]
# Load from list in json string
f_list2 = parse_raw_as(list[Foo], '[{"count": 130}, {"count": 140}]')
print(f_list2)
# OUT: [Foo(count=130, size=None), Foo(count=140, size=None)]
Complex hierarchical data structures
class Bar(BaseModel):
apple = "x"
banana = "y"
class Spam(BaseModel):
foo: Foo
bars: list[Bar]
m = Spam(foo={"count": 4}, bars=[{"apple": "x1"}, {"apple": "x2"}])
print(m)
# OUT: foo=Foo(count=4, size=None) bars=[Bar(apple='x1', banana='y'), Bar(apple='x2', banana='y')]
print(m.dict())
# OUT:
# {
# 'foo': {'count': 4, 'size': None},
# 'bars': [
# {'apple': 'x1', 'banana': 'y'},
# {'apple': 'x2', 'banana': 'y'},
# ],
# }
Pydantic supports many standard types (like datetime
) and special commonly used types (like EmailStr
and HttpUrl
):
from datetime import datetime
from pydantic import HttpUrl
class User(BaseModel):
name = "John Doe"
signup_ts: datetime = None
url: HttpUrl = None
u1 = User(signup_ts="2017-07-14 00:00:00")
print(u1)
# OUT: signup_ts=datetime.datetime(2017, 7, 14, 0, 0) url=None name='John Doe'
u2 = User(url="http://example.com")
print(u2)
# OUT: signup_ts=None url=HttpUrl('http://example.com', ) name='John Doe'
u3 = User(url="ht://example.com")
# OUT:
# ValidationError: 1 validation error for User
# url
# URL scheme not permitted (type=value_error.url.scheme; allowed_schemes={'http', 'https'})
If you really need to use json.dumps
, write a Custom Encoder:
import json
class EnhancedJSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, BaseModel):
return o.dict()
return super().default(o)
foo = Foo(count=20)
json.dumps([{"foo": foo}], cls=EnhancedJSONEncoder)
# OUT: '[{"foo": {"count": 20, "size": null}}]'
json_file_as_dict = json.load(path_to_json_file)
and thenFoo(**json_file_as_dict)
. – Waitress