Package: fastclasses-json
Fastclasses JSONInspired byDataclasses JSON.
This library attempts provide some basic functionality for encoding and
decodingdataclasseswith close to hand-written performance characteristics for large datasets.fromdataclassesimportdataclassfromfastclasses_jsonimportdataclass_json@dataclass_json@dataclassclassSimpleExample:str_field:strSimpleExample.from_dict({'str_field':'howdy!'})SimpleExample.from_json('{"str_field": "howdy!"}')# SimpleExample(str_field='howdy!')SimpleExample('hi!').to_dict()# {'str_field': 'hi!'}SimpleExample('hi!').to_json()# '{"str_field":"hi!"}'Installation$pipinstallfastclasses-jsonSupported Typestyping.List[T]whereTis also decorated with@dataclass_jsontyping.Optional[T]typing.Optional[typing.List[T]]typing.List[typing.Optional[T]]typing.List[typing.List[typing.List[T]]]etctyping.Dict[str, T]enum.Enumsubclassesdatetime.dateanddatetime.datetimeas ISO8601 format stringsNB: ifpython-dateutilis installed, it will be used instead of the
standard library for parsingdecimal.Decimalas stringsuuid.UUIDas stringsMutually recursive dataclasses.any other types will just be left as isfrom__future__importannotationsfromtypingimportOptional,List@dataclass_json@dataclassclassRussian:doll:Optional[Doll]@dataclass_json@dataclassclassDoll:russian:Optional[Russian]Russian.from_dict({'doll':{'russian':{'doll':None}}})# Russian(doll=Doll(russian=Russian(doll=None)))Russian(Doll(Russian(None))).to_dict()# {'doll': {'russian': {}}}fromenumimportEnumclassMood(Enum):HAPPY='json'SAD='xml'@dataclass_json@dataclassclassILikeEnums:maybe_moods:Optional[List[Mood]]ILikeEnums.from_dict({})# ILikeEnums(maybe_moods=None)ILikeEnums.from_dict({'maybe_moods':['json']})# ILikeEnums(maybe_moods=[Mood.HAPPY])ILikeEnums(maybe_moods=[Mood.HAPPY]).to_dict()# {'maybe_moods': ['json']}fromdatetimeimportdate@dataclass_json@dataclassclassEnitnelav:romantic:dateEnitnelav.from_dict({'romantic':'2021-06-17'})# Enitnelav(romantic=datetime.date(2021, 6, 17))Enitnelav(romantic=date(2021,6,17)).to_dict()# {'romantic': '2021-06-17'}fromdecimalimportDecimalfromuuidimportUUID@dataclass_json@dataclassclassTaxReturn:number:UUIDto_pay:Decimal# 😱TaxReturn.from_dict({'number':'e10be89e-938f-4b49-b4cf-9765f2f15298','to_pay':'0.01'})# TaxReturn(number=UUID('e10be89e-938f-4b49-b4cf-9765f2f15298'), to_pay=Decimal('0.01'))TaxReturn(UUID('e10be89e-938f-4b49-b4cf-9765f2f15298'),Decimal('0.01')).to_dict()# {'number': 'e10be89e-938f-4b49-b4cf-9765f2f15298', 'to_pay': '0.01'}we are not a drop-in replacement for Dataclasses JSON. There are plenty of
cases to use this in spite.ConfigurationPer-field configuration is done by including a"fastclasses_json"dict
in the field metadata dict.encoder: a function to convert a given field value when converting from
adataclassto adictor to JSON. Can be any callable.decoder: a function to convert a given field value when converting from
JSON or a dict into the pythondataclass. Can be any callable.field_name: the name the field should be called in the JSON output.example@dataclass_json@dataclassclassCoach:from_:str=field(metadata={"fastclasses_json":{"field_name":"from","encoder":lambdav:v[:5].upper(),}})to_:str=field(metadata={"fastclasses_json":{"field_name":"to","encoder":lambdav:v[:5].upper(),}})Coach("London Victoria","Amsterdam Sloterdijk").to_dict()# {'from': 'LONDO', 'to': 'AMSTE'}Whole tree configuration optionsHow to use other field naming conventionsThefield_name_transformoption allows tranforming field names of all
dataclasses that are serialized / deserialized.from__future__importannotationsfromfastclasses_jsonimportdataclass_jsonfromdataclassesimportdataclass@dataclass_json(field_name_transform=str.upper)@dataclassclassBox:dimensions:Dimensionsweight_in_g:int@dataclassclassDimensions:height_in_mm:intwidth_in_mm:intdepth_in_mm:intBox(Dimensions(12,24,35),944).to_dict()# {'DIMENSIONS': {'HEIGHT_IN_MM': 12, 'WIDTH_IN_MM': 24, 'DEPTH_IN_MM': 35}, 'WEIGHT_IN_G': 944}Type checking (i.e. using mypy)If using type annotations in your code, you may notice type errors when type
checking classes that use the@dataclass_jsondecorator.% mypy tests/for_type_checking.py
tests/for_type_checking.py:27: error: "A" has no attribute "to_json"
tests/for_type_checking.py:28: error: "Type[A]" has no attribute "from_dict"There are two techniques for overcoming this, one which is simpler but likely
to break or be unstable between versions of python and mypy; and one which
is a bit more work on your part.Mypy pluginChanges in python and mypy are likely to lead to a game of cat and mouse, but
for the moment, we have a plugin that you can configure in yoursetup.cfg% cat setup.cfg
[mypy]
plugins = fastclasses_json.mypy_pluginMixin with stub methodsThere is a mixin containing stub methods for converting to and from dicts and
JSON. This can be useful if the mypy plugin breaks or if you are using a
different type checker.fromdataclassesimportdataclassfromfastclasses_jsonimportdataclass_json,JSONMixin@dataclass_json@dataclassclassSimpleTypedExample(JSONMixin):what_a_lot_of_hassle_these_types_eh:strprint(SimpleTypedExample.from_dict({'what_a_lot_of_hassle_these_types_eh':'yes'}))% mypy that_listing_above.py
Success: no issues found in 1 source fileNotice that you have to use both the@dataclass_jsondecorator and theJSONMixinmixin. How very annoying!Migration & CaveatsNoneFields with the valueNoneare not included in the produced JSON. This helps
keep the JSON nice and compactfromdataclassesimportdataclassfromfastclasses_jsonimportdataclass_jsonfromtypingimportOptional@dataclass_json@dataclassclassFarm:sheep:Optional[int]cows:Optional[int]Farm(sheep=None,cows=1).to_json()# '{"cows":1}'infer_missingFastclasses JSON does not get annoyed if fields are missing when deserializing.
Missing fields are initialized asNone. This differs from the defaults inDataclasses JSON.fromdataclassesimportdataclassfromfastclasses_jsonimportdataclass_json@dataclass_json@dataclassclassCupboard:num_hats:intnum_coats:intCupboard.from_dict({'num_hats':2})# Cupboard(num_hats=2, num_coats=None)InDataclasses JSON, there is theinfer_missingparameter that gives this behaviour.
To make migration easier,from_dictandfrom_jsontakes the dummy parameterinfer_missing, so that the following code works the same and does
not cause errors:Cupboard.from_dict({'num_hats':2},infer_missing=True)# Cupboard(num_hats=2, num_coats=None)letter_caseFastclasses JSON does not haveletter_case, instead seefield_name_transformunderConfigurationwhich can achieve the same goals.