pytyped-json
pytyped-json is a Python package that enables automatic extraction of JSON decoders/encoders for given Python types.
pytyped-json is a piece of the pytyped collection of packages and follows its philosophy of using types to automate mundane and repetitive tasks.
Installation
You can install pytyped-json from PyPI:
pip install pytyped-json
pytyped-json is checked on Python 3.6+.
Why pytyped-json?
Based on the foundation of pytyped-macros, to our knowledge, pytyped-json is the only Python package that supports type-based JSON encoder/decoder extraction for all typing combinators including even recursive types that, up to this day, are not even fully supported by Python itself.
Additionally, pytyped-json is designed to be extensible.
That is, you can add your own specialized JSON decoders/encoders for either a simple type or even a generic type.
Currently, pytyped-json supports the following type driven JSON encoder/decoder extractions:
- JSON encoders/decoders for basic types such as
int,bool,date,datetime,str, andDecimal. - JSON encoders/decoders for simple type combinators such as
List[T]andDict[A, B]. - JSON encoders/decoders for named product types such as
NamedTuples ordataclasses. - JSON encoders/decoders for anonymous product types such as
Tuple[T1, T2, ...]. - JSON encoders/decoders for anonymous union types such as
Optional[T],Union[T1, T2, ...], etc. - JSON encoders/decoders for named union types such as class hierarchies (i.e., when a class
Ahas several subclassesA1, ...,An). - JSON encoders/decoders for generic types and type variables.
- JSON encoders/decoders for custom functional types such as
Set[T],Secret[T], etc where a custom function is defined for generic types such asSetorSecretand that functional is applied to all instantiations of those generic type. - JSON encoders/decoders for recursive types such as binary trees, etc.
Using pytyped-json to extract JSON decoders/encoders
First, define your type. For example, the following defines an account superclass that can either be a personal account or a business account. Here, we define a personal account to have one owner and possibly a co-owner while a business account is defined by the company name as the owner and a list of persons that can represent the company.
from dataclasses import dataclass
from datetime import datetime
from typing import List
from typing import Optional
@dataclass
class Person:
first_name: str
last_name: str
@dataclass
class Account:
created_at: datetime
@dataclass
class PersonalAccount(Account):
owner: Person
co_owner: Optional[Person]
@dataclass
class BusinessAccount(Account):
owner: str
representatives: List[Person]
Second, use an instance of AutoJsonDecoder and AutoJsonEncoder to extract JSON decoders and encoders as below:
from pytyped.json.decoder import AutoJsonDecoder
from pytyped.json.decoder import JsonDecoder
from pytyped.json.encoder import AutoJsonEncoder
from pytyped.json.encoder import JsonEncoder
_auto_json_decoder = AutoJsonDecoder()
_auto_json_encoder = AutoJsonEncoder()
account_decoder: JsonDecoder[Account] = _auto_json_decoder.extract(Account)
account_encoder: JsonEncoder[Account] = _auto_json_encoder.extract(Account)
Third, define some instances of the Account class:
personal_account = PersonalAccount(
created_at = datetime.now(),
owner = Person(first_name = "John", last_name = "Doe"),
co_owner = None
)
business_account = BusinessAccount(
created_at = datetime.now(),
owner = "Doe Ltd.",
representatives = [Person(first_name = "John", last_name = "Doe"), Person(first_name = "Jane", last_name = "Doe")]
)
Finally, use account_encoder and account_decoder to convert data in your instances to/from JSON as below:
>>> json = account_encoder.write(personal_account)
>>> json
{'created_at': '2020-08-24T20:00:18.205347', 'owner': {'first_name': 'John', 'last_name': 'Doe'}, 'co_owner': None, 'Account': 'PersonalAccount'}
>>> account_decoder.read(json)
PersonalAccount(created_at=datetime.datetime(2020, 8, 24, 20, 0, 18, 205347), owner=Person(first_name='John', last_name='Doe'), co_owner=None)
>>> json = account_encoder.write(business_account)
>>> json
{'created_at': '2020-08-24T20:00:40.057088', 'owner': 'Doe Ltd.', 'representatives': [{'first_name': 'John', 'last_name': 'Doe'}, {'first_name': 'Jane', 'last_name': 'Doe'}], 'Account': 'BusinessAccount'}
>>> account_decoder.read(json)
BusinessAccount(created_at=datetime.datetime(2020, 8, 24, 20, 0, 40, 57088), owner='Doe Ltd.', representatives=[Person(first_name='John', last_name='Doe'), Person(first_name='Jane', last_name='Doe')])
To illustrate the types of validation that JSON decoders enable for you, consider the following example invalid JSONs:
>>> account_decoder.read({'created_at': '2020-08-24T20:00:40.057088', 'owner': 'Doe Ltd.', 'representatives': [{'first_name': 'John', 'last_name': 'Doe'}, {'first_name': 'Jane', 'last_name': 'Doe'}], 'Account': 'NewAccount'})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "~/Repos/pytyped/pytyped/json/decoder.py", line 91, in read
raise JsDecodeException(t_or_error)
pytyped.json.decoder.JsDecodeException: Found 1 errors while validating JSON: [
Error when decoding JSON: /Account: Unknown tag value NewAccount (possible values are: PersonalAccount, BusinessAccount).]
>>> account_decoder.read({'created_at': '2020-08-24T20:00:40.057088', 'owner': 'Doe Ltd.', 'representatives': [{'first_name': 'John', 'last_name': 'Doe'}, {'first_name': 'Jane', 'last': 'Doe'}], 'Account': 'BusinessAccount'})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/shahab/Repos/pytyped/pytyped/json/decoder.py", line 91, in read
raise JsDecodeException(t_or_error)
pytyped.json.decoder.JsDecodeException: Found 1 errors while validating JSON: [
Error when decoding JSON: /representatives[1]/last_name: Non-optional field was not found]
Issues
Please report any issues to the GitHub repository for this package.
Contributors
Release files for pytyped-json 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytyped-json-2.0.0.tar.gz | 11.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytyped_json-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.8 kB
Release files / pytyped-json-2.0.0.tar.gz
| Download URL | pytyped-json-2.0.0.tar.gz |
|---|---|
| Size | 11.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4b2322aef91916685298bef951a1fc76b05b8c1ff93afc24e4a3621ce3adefc1
|
|
BLAKE2b-256 checksum How to use checksums |
40c63d015968295cd74dda26a514c84c4e84658670a408b35ba9e9e8d5735d94
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.0 CPython/3.9.16
|
Release files / pytyped_json-2.0.0-py3-none-any.whl
| Download URL | pytyped_json-2.0.0-py3-none-any.whl |
|---|---|
| Size | 11.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
97ec26c92a07c70f789f46989f30b8b622e7323abc18ae545b5dd5b3271d062d
|
|
BLAKE2b-256 checksum How to use checksums |
46d2a94afb149dba9a49cff5027edc17030da6f1528cdd19366d2c7cf296aa4e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.0 CPython/3.9.16
|