Skip to main content

Fastclasses JSON

CI PyPI

Inspired by Dataclasses JSON. This library attempts provide some basic functionality for encoding and decoding dataclasses with close to hand-written performance characteristics for large datasets.

from dataclasses import dataclass
from fastclasses_json import dataclass_json

@dataclass_json
@dataclass
class SimpleExample:
    str_field: str

SimpleExample.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

$ pip install fastclasses-json

Supported Types

  • typing.List[T] where T is also decorated with @dataclass_json
  • typing.Optional[T]
  • typing.Optional[typing.List[T]]
  • typing.List[typing.Optional[T]]
  • typing.List[typing.List[typing.List[T]]] etc
  • typing.Dict[str, T]
  • enum.Enum subclasses
  • datetime.date and datetime.datetime as ISO8601 format strings
    • NB: if python-dateutil is installed, it will be used instead of the standard library for parsing
  • decimal.Decimal as strings
  • uuid.UUID as strings
  • Mutually recursive dataclasses.

any other types will just be left as is

from __future__ import annotations
from typing import Optional, List

@dataclass_json
@dataclass
class Russian:
    doll: Optional[Doll]

@dataclass_json
@dataclass
class Doll:
    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': {}}}

from enum import Enum

class Mood(Enum):
    HAPPY = 'json'
    SAD = 'xml'

@dataclass_json
@dataclass
class ILikeEnums:
    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']}

from datetime import date

@dataclass_json
@dataclass
class Enitnelav:
    romantic: date

Enitnelav.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'}

from decimal import Decimal
from uuid import UUID

@dataclass_json
@dataclass
class TaxReturn:
    number: UUID
    to_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.

Configuration

Per-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 a dataclass to a dict or to JSON. Can be any callable.
  • decoder: a function to convert a given field value when converting from JSON or a dict into the python dataclass. Can be any callable.
  • field_name: the name the field should be called in the JSON output.

example

@dataclass_json
@dataclass
class Coach:
    from_: str = field(metadata={
        "fastclasses_json": {
            "field_name": "from",
            "encoder": lambda v: v[:5].upper(),
        }
    })
    to_: str = field(metadata={
        "fastclasses_json": {
            "field_name": "to",
            "encoder": lambda v: v[:5].upper(),
        }
    })


Coach("London Victoria", "Amsterdam Sloterdijk").to_dict()
# {'from': 'LONDO', 'to': 'AMSTE'}

Whole tree configuration options

How to use other field naming conventions

The field_name_transform option allows tranforming field names of all dataclasses that are serialized / deserialized.

from __future__ import annotations
from fastclasses_json import dataclass_json
from dataclasses import dataclass

@dataclass_json(field_name_transform=str.upper)
@dataclass
class Box:
    dimensions: Dimensions
    weight_in_g: int

@dataclass
class Dimensions:
    height_in_mm: int
    width_in_mm: int
    depth_in_mm: int

Box(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_json decorator.

% 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 plugin

Changes 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 your setup.cfg

% cat setup.cfg
[mypy]
plugins = fastclasses_json.mypy_plugin

Mixin with stub methods

There 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.

from dataclasses import dataclass
from fastclasses_json import dataclass_json, JSONMixin

@dataclass_json
@dataclass
class SimpleTypedExample(JSONMixin):
    what_a_lot_of_hassle_these_types_eh: str

print(SimpleTypedExample.from_dict({'what_a_lot_of_hassle_these_types_eh': 'yes'}))
% mypy that_listing_above.py
Success: no issues found in 1 source file

Notice that you have to use both the @dataclass_json decorator and the JSONMixin mixin. How very annoying!

Migration & Caveats

None

Fields with the value None are not included in the produced JSON. This helps keep the JSON nice and compact

from dataclasses import dataclass
from fastclasses_json import dataclass_json
from typing import Optional

@dataclass_json
@dataclass
class Farm:
    sheep: Optional[int]
    cows: Optional[int]

Farm(sheep=None, cows=1).to_json()
# '{"cows":1}'

infer_missing

Fastclasses JSON does not get annoyed if fields are missing when deserializing. Missing fields are initialized as None. This differs from the defaults in Dataclasses JSON.

from dataclasses import dataclass
from fastclasses_json import dataclass_json

@dataclass_json
@dataclass
class Cupboard:
    num_hats: int
    num_coats: int

Cupboard.from_dict({'num_hats': 2})
# Cupboard(num_hats=2, num_coats=None)

In Dataclasses JSON, there is the infer_missing parameter that gives this behaviour. To make migration easier, from_dict and from_json takes the dummy parameter infer_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_case

Fastclasses JSON does not have letter_case, instead see field_name_transform under Configuration which can achieve the same goals.

Metadata

Release files for fastclasses-json 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastclasses-json 0.8.0
File Size Uploaded
fastclasses_json-0.8.0.tar.gz 20.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fastclasses-json 0.8.0
File Interpreter ABI Platform
fastclasses_json-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 33.2 kB

Release files / fastclasses_json-0.8.0.tar.gz

Download URL fastclasses_json-0.8.0.tar.gz
Size 20.5 kB
Tags Source
SHA-256 checksum
How to use checksums
4f06cd559a4e752e31c0ae04622d1c7abb5a66fa53b550c262d6de49082225a0
BLAKE2b-256 checksum
How to use checksums
df9d633477c3d6a9ad0ef73a30ca5abbe65aed2b639d2e5c2b9d1108b28d7510
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.8.18

Release files / fastclasses_json-0.8.0-py3-none-any.whl

Download URL fastclasses_json-0.8.0-py3-none-any.whl
Size 12.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e9e963a3bd1aa44e97c050c1af1cdb18578bfa0e586463982fe3781dade7d579
BLAKE2b-256 checksum
How to use checksums
67b7d292d32bd44c4e1b7ee5e50144197d72d64f25d976a28a2af3eec89ad3b0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.8.18
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page