Skip to main content

Convert JSON to Python dataclasses or namedtuples

Project description

json-to-py

json-to-py is a lightweight Python utility that simplifies converting JSON data into Python dataclass instances. It leverages Python's type annotations to ensure that JSON structures are parsed into strongly-typed Python objects, enhancing code reliability and maintainability.

Features

  • 🔍 Type-Safe Parsing: Automatically converts JSON values into instances of specified types, ensuring type correctness.

  • 🧩 Nested Structure Support: Seamlessly handles nested types, allowing for complex JSON structures to be parsed effortlessly.

  • 🛠️ Minimal Dependencies: Built using Python's standard libraries, eliminating the need for external packages.

Installation

You can install json-to-py via pip:

pip install json-to-py

Usage

Here's a basic example demonstrating how to use json-to-py:

from dataclasses import dataclass
from json_to_py import parse_json

@dataclass
class Address:
    street: str
    city: str
    postal_code: str

@dataclass
class Person:
    name: str
    age: int
    address: Address

json_data = {
    "name": "Alice",
    "age": 30,
    "address": {
        "street": "123 Main St",
        "city": "Wonderland",
        "postal_code": "12345"
    }
}

person = parse_json(json_data, Person)
print(person)

# Output: Person(name='Alice', age=30, address=Address(street='123 Main St', city='Wonderland', postal_code='12345'))

API Reference

parse_json(data: JSONType, clazz: Type[T]) -> T

Parses a JSON-compatible value (data) into an instance of the specified dataclass (clazz).

Limitations

  • The target class must be a
    • str
    • int
    • float
    • bool
    • typing.Any
    • typing.Dict of key type str
    • typing.List
    • typing.Set
    • typing.Tuple
    • typing.Optional
    • typing.Literal / typing_extensions.Literal
    • typing.Union
    • Dataclass
    • NamedTuple
  • All fields in the dataclass/namedtuple must be type-annotated
  • Setting default values

Features

  • Works with Python 3.7+
  • Have different names in the json from a dataclass:
    @dataclass
    class Example():
        a_number: int = field(metadata={"json-to-py": "a-number"})
    
    Here, the key in the JSON must be a-number instead of an a_number.
  • Support for typing.Dict:
    • The key type has to be str, the value can be any of the listed in the limitations section
  • Support for typing.List:
    • The list type can be any of the listed in the limitations section
  • Support for typing.Set:
    • Interpreted as a JSON array
    • The set type must be any of the listed in the limitations section
  • Support for typing.Tuple:
    • Interpreted as a JSON array with a fixed amount of elements
    • The tuple types must be any of the listed in the limitations section
  • Support for typing.Literal / typing_extensions.Literal:
    • Checks if the JSON value is in the literal list of values
  • Support for typing.Union
    • The union types must be any of the listed in the limitations section
    • To parse unions, the first type defined in the union is assumed to be the correct one and attemped to parse. If this fails, the next type is tryed until there are no more types or one succeeds

More complex example

See the example below for an example with versioning and lots of features

class UserInformation(NamedTuple):
    name: str
    age: int

data = {
    "name": "Nemo",
    "age": 64
}
print(parse_json(data), UserInformation)
# UserInformation(
#   name='Nemo',
#   age=64
# )

class UserInformation_v2(NamedTuple):
    version: Literal["1.2"] # Added to differenciate between versions
    name: str
    surenames: List[str] # Added
    age: int

data = {
    "version": "1.2",
    "name": "Nemo",
    "surenames": ["First", "Seccond"],
    "age": 64
}
print(parse_json(data), Union[UserInformation_v2, UserInformation])
# UserInformation_v2(
#   version='1.2',
#   name='Nemo',
#   surenames=['First', 'Seccond'],
#   age=64
# )

class UserInformation_v3(NamedTuple):
    version: Literal["1.3"]
    name: List[str] # Merged name with surenames
    age: float # Changed from int to float

data = {
    "version": "1.3",
    "name": ["Nemo", "First", "Seccond"],
    "age": 64.5
}
print(parse_json(data), Union[UserInformation_v3, UserInformation_v2, UserInformation])
# UserInformation_v3(
#   version='1.3',
#   name=['Nemo', 'First', 'Seccond'],
#   age=64.5
# )

@dataclass # Switched to dataclass
class UserInformation_v4():
    version: Literal["1.4"]
    id: str # Added
    name: List[str]
    age: float
    relations: List[str] # Added

data = {
    "version": "1.4",
    "id": "id123",
    "name": ["Nemo", "First", "Seccond"],
    "age": 64.5,
    "relations": ["id456"]
}
print(parse_json(data), Union[UserInformation_v4, UserInformation_v3, UserInformation_v2, UserInformation])
# UserInformation_v4(
#   version='1.4',
#   id='id123',
#   name=['Nemo', 'First', 'Seccond'],
#   age=64.5,
#   relations=['id456']
# )

@dataclass
class UserRelation():
    user: str
    relation_type: str = field(metadata={"json-to-py": {"name": "relation-type"}}) # Will apprear in the json as 'relation-type'

@dataclass
class UserInformation_v5():
    version: Literal["1.5"]
    id: str
    name: List[str]
    age: float
    relations: List[UserRelation] # Changed to UserRelation

data = {
    "version": "1.5",
    "id": "id123",
    "name": ["Nemo", "First", "Seccond"],
    "age": 64.5,
    "relations": ["id456"]
}
print(parse_json(data), Union[UserInformation_v5, UserInformation_v4, UserInformation_v3, UserInformation_v2, UserInformation])
# UserInformation_v5(
#   version='1.5',
#   id='id123',
#   name=['Nemo', 'First', 'Seccond'],
#   age=64.5,
#   relations=[
#       UserRelation(user='id456', relation_type='friend')
#   ]
# )

@dataclass
class UserInformation_v6():
    version: Literal["1.6"]
    id: str
    name: List[str]
    age: float
    relations: List[UserRelation]
    extra_information: Dict[str, str] = field(metadata={"json-to-py": {"name": "extra-information"}}) # Added. Will apprear in the json as 'extra-information'

data = {
    "version": "1.6",
    "id": "id123",
    "name": ["Nemo", "First", "Seccond"],
    "age": 64.5,
    "relations": [{"user": "id456", "relation-type": "friend"}],
    "extra-information": {"extra": "info"}
}
print(parse_json(data), Union[UserInformation_v6, UserInformation_v5, UserInformation_v4, UserInformation_v3, UserInformation_v2, UserInformation])
# UserInformation_v6(
#   version='1.6',
#   id='id123',
#   name=['Nemo', 'First', 'Seccond'],
#   age=64.5,
#   relations=[
#       UserRelation(user='id456', relation_type='friend')
#   ],
#   extra_information={
#       'extra': 'info'
#    }
# )

@dataclass
class UserRelation_v2():
    version: Literal["1.2"] # Added to differenciate between versions
    user: str
    since: str
    relation_type: str = field(metadata={"json-to-py": {"name": "relation-type"}})

class UserFieldInt(NamedTuple):
    name: str
    value: int

class UserFieldStr(NamedTuple):
    name: str
    value: str

class UserFieldBool(NamedTuple):
    name: str
    value: bool

@dataclass
class UserInformation_v7():
    version: Literal["1.7"]
    id: str
    name: List[str]
    age: float
    relations: List[UserRelation_v2] # Changed
    extra_information: List[Union[UserFieldInt, UserFieldStr, UserFieldBool]] = field(metadata={"json-to-py": {"name": "extra-information"}}) # Changed

data = {
    "version": "1.7",
    "id": "id123",
    "name": ["Nemo", "First", "Seccond"],
    "age": 64.5,
    "relations": [{"version": "1.2", "user": "id456", "since": "11-05-2025", "relation-type": "friend"}],
    "extra-information": [{"name": "a string", "value": "string value"}, {"name": "an int", "value": 987}, {"name": "a bool", "value": True}]
}
print(parse_json(data), Union[UserInformation_v7, UserInformation_v6, UserInformation_v5, UserInformation_v4, UserInformation_v3, UserInformation_v2, UserInformation])
# UserInformation_v7(
#   version='1.7',
#   id='id123',
#   name=['Nemo', 'First', 'Seccond'],
#   age=64.5,
#   relations=[
#       UserRelation_v2(version='1.2', user='id456', since='11-05-2025', relation_type='friend')
#   ],
#   extra_information=[
#       UserFieldStr(name='a string', value='string value'),
#       UserFieldInt(name='an int', value=987),
#       UserFieldBool(name='a bool', value=True)
#   ]
# )

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

json_to_py-1.0.0.tar.gz (14.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

json_to_py-1.0.0-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file json_to_py-1.0.0.tar.gz.

File metadata

  • Download URL: json_to_py-1.0.0.tar.gz
  • Upload date:
  • Size: 14.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for json_to_py-1.0.0.tar.gz
Algorithm Hash digest
SHA256 adc67d011f411c008c0d630389f71d4b64bac139fc3823c9758d4ac9a0562509
MD5 ecaaaae4c3bf9e4df916057d92747b8c
BLAKE2b-256 aeec9e60585bb221e2230889f8213dcf8830e8ffe690b80f25899eedf178c94e

See more details on using hashes here.

File details

Details for the file json_to_py-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: json_to_py-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.12

File hashes

Hashes for json_to_py-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ff13691b9b7d16ef99fc24cd0bda1bb4aef7bebe49946e65620e4c5b3376c42f
MD5 d9b201494609f28b95acf979958fe620
BLAKE2b-256 7fe7001bd2dbe83d0f6fae6b9b56487dba6844fa77ac4df59ca4aec6c9dcfee9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page