Type hinting and validation for JSON-serializable Python data types.
Project description
json-data-types
json-data-types provides type aliases, validators, and a flexible object-oriented validation framework for Python values that are directly serializable to JSON — scalars, dicts, and lists, in both strict (dict/list) and duck-typed (Mapping/Sequence) flavors.
Highlights
JsonData,JsonDict,JsonList— strict type aliases requiring realdictandlistobjects.JsonDataLike,JsonDictLike,JsonListLike— duck-typed aliases accepting anyMappingorSequence.JsonScalar— the union of the five JSON primitive types (int,float,str,bool,None).JsonScalarTypes— a runtimetuple[type, ...]constant suitable forisinstance()checks.- Validator functions —
validate_json_*andloads_json_*for each type alias, with deep or shallow validation and cyclic-reference detection. JsonObjectValidator— an extensible base class for building custom validators via subclassing.NotJsonDataError— aValueErrorsubclass raised on validation failure.- Zero dependencies — pure Python, no third-party packages required.
- Fully typed — PEP 561 compliant, inline annotations, works with mypy strict mode.
Installation
pip install json-data-types
Type Hierarchy
JsonScalar int | float | str | bool | None
JsonDataLike JsonScalar | JsonListLike | JsonDictLike
JsonListLike Sequence[JsonDataLike] # any Sequence (not str/bytes)
JsonDictLike Mapping[str, JsonDataLike] # any Mapping with str keys
JsonData JsonScalar | JsonList | JsonDict
JsonList list[JsonData] # real list only
JsonDict dict[str, JsonData] # real dict only
Use the *Like variants when accepting data that may come from custom Mapping or Sequence implementations (e.g., collections.OrderedDict, types.MappingProxyType, tuple). Use the strict variants (JsonData, JsonDict, JsonList) when you need plain Python dict and list objects — the most common case when working with json.loads() output.
Quick Start
from json_data_types import JsonData, JsonDict, validate_json_data
# Annotate function parameters and return types
def process(payload: JsonData) -> JsonDict:
assert isinstance(payload, dict)
return {"status": "ok", "echo": payload}
# Validate and typecast untrusted data at a boundary
raw: object = {"key": [1, True, None, "hello"]}
data: JsonData = validate_json_data(raw) # raises NotJsonDataError if invalid
Type Aliases
JsonScalar
JsonScalar: TypeAlias = int | float | str | bool | None
Any JSON primitive value. Maps to JSON's number (integers and floats), string, boolean, and null.
JsonScalarTypes
JsonScalarTypes: Final[tuple[type, ...]] = (int, float, str, bool, NoneType)
A runtime constant holding the same types as JsonScalar, usable with isinstance():
from json_data_types import JsonScalarTypes
isinstance(42, JsonScalarTypes) # True
isinstance([1, 2], JsonScalarTypes) # False
JsonData
JsonData: TypeAlias = JsonScalar | JsonList | JsonDict
Any fully JSON-serializable Python value built exclusively from dict, list, and the scalar types. This is the type you get back from json.loads().
JsonDict
JsonDict: TypeAlias = dict[str, JsonData]
A dict mapping str keys to JsonData values.
JsonList
JsonList: TypeAlias = list[JsonData]
A list of JsonData values.
JsonDataLike
JsonDataLike: TypeAlias = JsonScalar | JsonListLike | JsonDictLike
Like JsonData but accepts any Mapping or Sequence rather than only dict and list. Useful at API boundaries where callers may pass OrderedDict, MappingProxyType, named tuples, etc.
JsonDictLike
JsonDictLike: TypeAlias = Mapping[str, JsonDataLike]
Any Mapping with str keys and JsonDataLike values.
JsonListLike
JsonListLike: TypeAlias = Sequence[JsonDataLike]
Any Sequence of JsonDataLike values. str is excluded — it is treated as a scalar (JsonScalar), not as a sequence of characters.
NotJsonDataError
All validators raise NotJsonDataError on failure. It is a subclass of ValueError, so existing except ValueError clauses continue to work.
from json_data_types import NotJsonDataError, validate_json_data
try:
validate_json_data((1, 2, 3))
except NotJsonDataError as e:
print(e) # Invalid top-level JSON data: (1, 2, 3) ...
Validator Functions
validate_json_data(data, fast=False) -> JsonData
Validates that data is a pure-Python JSON-serializable value. Raises NotJsonDataError if any value is not of an allowed type, if any dict key is not a str, or if a cyclic reference is detected.
from json_data_types import validate_json_data
data = validate_json_data({"users": [{"id": 1, "score": 9.5, "active": True}]})
# data is typed as JsonData
With fast=True, only the top-level type is checked — no recursion, no cycle detection. Useful when you trust the structure but need a quick typecast.
data = validate_json_data(payload, fast=True) # shallow check only
validate_json_dict(data, fast=False) -> JsonDict
Validates that data is a dict with str keys and JsonData values.
cfg: JsonDict = validate_json_dict(raw_config)
validate_json_list(data, fast=False) -> JsonList
Validates that data is a list of JsonData values.
items: JsonList = validate_json_list(raw_items)
validate_json_data_like(data, fast=False) -> JsonDataLike
Like validate_json_data but accepts any Mapping or Sequence rather than only dict and list.
from collections import OrderedDict
from json_data_types import validate_json_data_like
od = OrderedDict([("a", 1), ("b", [2, 3])])
result = validate_json_data_like(od) # passes
validate_json_dict_like(data, fast=False) -> JsonDictLike
Validates that data is a Mapping with str keys and JsonDataLike values. Accepts any Mapping subtype at all nesting levels.
from types import MappingProxyType
from json_data_types import validate_json_dict_like
mp = MappingProxyType({"key": [1, 2, 3]})
result = validate_json_dict_like(mp) # passes
validate_json_list_like(data, fast=False) -> JsonListLike
Validates that data is a Sequence of JsonDataLike values. str is excluded. Accepts any Sequence subtype (including tuple) at all nesting levels.
from json_data_types import validate_json_list_like
result = validate_json_list_like((1, True, None, {"a": 1})) # passes
loads_json_* Functions
Each validator has a corresponding loads_json_* function that combines json.loads() with validation, returning the result cast to the appropriate type:
| Function | Returns |
|---|---|
loads_json_data(s, *, fast=False, **kwargs) |
JsonData |
loads_json_dict(s, *, fast=False, **kwargs) |
JsonDict |
loads_json_list(s, *, fast=False, **kwargs) |
JsonList |
loads_json_data_like(s, *, fast=False, **kwargs) |
JsonDataLike |
loads_json_dict_like(s, *, fast=False, **kwargs) |
JsonDictLike |
loads_json_list_like(s, *, fast=False, **kwargs) |
JsonListLike |
s may be str, bytes, or bytearray. Extra keyword arguments are forwarded to json.loads().
from json_data_types import loads_json_dict
config: JsonDict = loads_json_dict(response.content)
Object-Oriented Validator API
Each validator is an instance of a concrete subclass of JsonObjectValidator. The singleton instances used by the module-level functions are:
| Instance | Class |
|---|---|
(used by validate_json_data) |
JsonDataValidator |
(used by validate_json_dict) |
JsonDictValidator |
(used by validate_json_list) |
JsonListValidator |
(used by validate_json_data_like) |
JsonDataLikeValidator |
(used by validate_json_dict_like) |
JsonDictLikeValidator |
(used by validate_json_list_like) |
JsonListLikeValidator |
Validator instances are callable (__call__) and have a loads() method, both with the same signature as the module-level functions. You can create your own instances directly:
from json_data_types import JsonDataValidator, NotJsonDataError
v = JsonDataValidator()
data = v(raw) # same as validate_json_data(raw)
data = v.loads(text) # same as loads_json_data(text)
Subclassing JsonObjectValidator
JsonObjectValidator[_T] is a generic base class. Override class variables to customize which types are accepted at each level:
| Class variable | Default | Purpose |
|---|---|---|
_scalar_types |
JsonScalarTypes |
Types treated as scalars |
_dict_types |
(dict,) |
Types treated as JSON objects |
_list_types |
(list,) |
Types treated as JSON arrays |
_dict_key_types |
(str,) |
Allowed dict key types |
_toplevel_types |
None (same as allowed) |
Restrict the top-level type |
_excluded_types |
(bytes, bytearray) |
Always-excluded types |
_excluded_scalar_types |
() |
Excluded from scalar check |
_excluded_dict_types |
() |
Excluded from dict check |
_excluded_list_types |
(str, bytes, bytearray) |
Excluded from list check |
_excluded_toplevel_types |
() |
Excluded at top level only |
Example — a validator that only accepts dict at the top level but uses duck-typed Mapping/Sequence checks internally:
from collections.abc import Mapping, Sequence
from json_data_types import JsonObjectValidator, JsonDictLike
class StrictTopDuckDeepValidator(JsonObjectValidator[JsonDictLike]):
_toplevel_types = (dict,) # top level must be a real dict
_dict_types = (Mapping,) # nested dicts may be any Mapping
_list_types = (Sequence,) # nested lists may be any Sequence
v = StrictTopDuckDeepValidator()
Common Patterns
Validating API input
from json_data_types import JsonDict, loads_json_dict
def handle_request(body: bytes) -> JsonDict:
return loads_json_dict(body) # parses + validates in one call
Accepting flexible input
from json_data_types import JsonDataLike, validate_json_data_like
import json
def serialize(value: JsonDataLike) -> str:
validate_json_data_like(value) # raises NotJsonDataError if invalid
return json.dumps(value)
Detecting cyclic structures before serialization
from json_data_types import validate_json_data
bad: dict = {}
bad["self"] = bad # cyclic!
validate_json_data(bad)
# NotJsonDataError: Cyclic reference detected in JsonDataLike object
Using fast=True for trusted data
from json_data_types import JsonData, validate_json_data
# Already validated upstream; just need the typecast
data: JsonData = validate_json_data(trusted_payload, fast=True)
Checking scalar types at runtime
from json_data_types import JsonScalarTypes
def is_json_scalar(v: object) -> bool:
return isinstance(v, JsonScalarTypes)
Supported Python Versions
Python 3.10 through 3.14.
License
MIT. See LICENSE.
For development and release workflow documentation, see CONTRIBUTING.md.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file json_data_types-0.2.0.tar.gz.
File metadata
- Download URL: json_data_types-0.2.0.tar.gz
- Upload date:
- Size: 15.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
479ecfd81358ea704980b1faf579aef452afb09212dc9f636b068eaf4a426529
|
|
| MD5 |
d859cdb5cf221e867d49b1dbf8e07379
|
|
| BLAKE2b-256 |
045f95640f9ef57cc510918851b2fe1100365614a0b838f4945451e7e099a51e
|
Provenance
The following attestation bundles were made for json_data_types-0.2.0.tar.gz:
Publisher:
publish.yml on mckelvie-org/json-data-types
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
json_data_types-0.2.0.tar.gz -
Subject digest:
479ecfd81358ea704980b1faf579aef452afb09212dc9f636b068eaf4a426529 - Sigstore transparency entry: 1971649066
- Sigstore integration time:
-
Permalink:
mckelvie-org/json-data-types@e3c9b6b9f57235ed3f9b3378861ab65f4c090a2a -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mckelvie-org
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e3c9b6b9f57235ed3f9b3378861ab65f4c090a2a -
Trigger Event:
push
-
Statement type:
File details
Details for the file json_data_types-0.2.0-py3-none-any.whl.
File metadata
- Download URL: json_data_types-0.2.0-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65fb77313999287a3bea1a56988acf3c82b30e3bb81265979ad6428c63eb28ae
|
|
| MD5 |
ed939ff7f939cf160e2a38fb4c3d3b49
|
|
| BLAKE2b-256 |
57d8717e3d5db4cfaa689420bf50dee89a4e97b04b0215776556b07d9e961350
|
Provenance
The following attestation bundles were made for json_data_types-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on mckelvie-org/json-data-types
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
json_data_types-0.2.0-py3-none-any.whl -
Subject digest:
65fb77313999287a3bea1a56988acf3c82b30e3bb81265979ad6428c63eb28ae - Sigstore transparency entry: 1971649117
- Sigstore integration time:
-
Permalink:
mckelvie-org/json-data-types@e3c9b6b9f57235ed3f9b3378861ab65f4c090a2a -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/mckelvie-org
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e3c9b6b9f57235ed3f9b3378861ab65f4c090a2a -
Trigger Event:
push
-
Statement type: