| Name | SlySerialize JSON |
| Version |
0.0.2
JSON |
| download |
| home_page | |
| Summary | Convert JSON-like data structures into nice Python objects. |
| upload_time | 2023-08-12 06:34:02 |
| maintainer | |
| docs_url | None |
| author | Dunkyl 🔣🔣 |
| requires_python | >=3.10 |
| license | MIT License Copyright (c) 2023 Maroue Reus Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| keywords |
|
| VCS |
 |
| bugtrack_url |
|
| requirements |
No requirements were recorded.
|
| Travis-CI |
No Travis.
|
| coveralls test coverage |
No coveralls.
|
#  Sly Serialize for Python
Convert JSON-like data structures into nice Python objects.
Key features:
- Common, built-in types like `set`, `tuple` and `Enum`
- Generic dataclasses and nested generics
- Type aliases
- Union types
- Recursive types and delayed annotations
- Custom deserialization
- Asynchronous custom deserialization
- Zero dependencies
In just one line:
```py
assert myThing == from_json(MyClass[int], to_json(myThing))
```
## Install
```shell
pip install slyserialize
```
## Basic usage
Call `from_json` with a target type, with generic arguments, and some json data, such as returned from `json.loads`. Generic arguments are optional, but if you don't provide them, you'll get a `TypeError` if the target type requires them to be concrete. See the final line in the following example:
```py
from typing import Generic, TypeVar, TypeAlias
from dataclasses import dataclass
from SlySerialize import from_json
ListOfIntegers: TypeAlias = list[int]
T = TypeVar("T")
@dataclass
class MyClass(Generic[T]):
aliased: ListOfIntegers
generic: T
builtin: tuple[float, list[str]]
union: dict[str, T] | None
delayed: 'MyClass[T] | None'
my_obj = MyClass[int]([1, 2, 3], 42, (3.1, ["a"]), None, None)
# dataclasses.asdict(my_obj)
serialized = {
"aliased": [1, 2, 3], "generic": 42,
"union": None, "delayed": None,
"builtin": [3.1, ["a"]],
}
assert my_obj == from_json(MyClass[int], serialized)
```
## Should I use this?
The goal of this library is to handle deserialization cases for strongly typed dataclass and custom types, and to do so with as little code as possible.
If you only want fast and customizable *serialization*, but maybe not deserialization, you should use a library like [orjson](https://pypi.org/project/orjson/).
If you are using JSON-like types already, without generics or some other specific feature, there are also other libraries that will deserialize faster.
The serialization format for JSON in this library also prefers succinctness at the cost of certain edge cases related to overlapping type representations. See the [Default Representations](#default-representations) section for more details.
## Notes
Type variables or mutually recursive types must be declared in the global scope if used in a delayed annotation.
`SlySerialize.JsonType` is an alias for the return type of `json.loads` (Python standard library), it represents the native JSON types.
If the type is not supported, or if the representation was different than what was expected, a `TypeError` will be raised.
`dataclasses.asdict` only supports serializing dataclasses, `JsonType`, `tuple`, and lists, tuples, or dicts of these. There will not be an error, but, the return type is *not* `JsonType`. Other types will be passed through unaffected. If you want to serialize a dataclass with a member that is not one of these types, you may want to implement your own serialization.
## Default Representations
The following types are supported by default:
- `NoneType`, `bool`, `int`, `float`, and `str` as themselves
- `list` and `dict` as arrays and maps, with or without their generic arguments
- `dict` is only supported if the key type is `str`
- The default value type used with no arguments is `JsonType`
- `tuple` and `set` as an array
- Dataclasses as maps
- Enum types of `str` or `int` as their value
- Union types as whichever case the value would otherwise be represented as
- Generic types are substituted for their concrete type. If the type is not available, a `ValueError` is raised.
- The covariant versions of `list` and `map`, `collections.abc.Sequence` and `collections.abc.Mapping`, are also supported.
- Consequently, `JsonType` is a valid type to deserialize to. If you want to do nothing to a member during deserialization, use `JsonType` as the type.
Not all types are guaranteed to round-trip. For a simple example, `list` and `set` are both serialized as a JSON array, so if there is a union `list | set`, and an empty value, then the first case, `list`, will be selected during deserialization. Other examples would include `dict` and classes, or classes that have the same member names.
## Serialization
Implementations for serialization is provided. Some type loaders that are needed for deserialization do not have a corresponding serializer, such as `TypeVar` and `TypeAlias`, since it would be unusual for an instance value to be these types.
```py
# ...
from SlySerialize import to_json
assert serialized == to_json(my_obj)
```
In most cases, `to_json` should be the inverse of `from_json`.
## Custom Converters
Custom converters can be created for any type. Subclass `Loader` or `Unloader`, or `Converter` for both. These have a generic argument for the domain type that they serialize to or from. For JSON, this is `JsonType`. Some of the loaders implemented by this library do not require a specific domain, so they may be combined with custom loaders for other domain types.
Loaders must implement the following two methods:
```py
def can_load(self, cls: type) -> bool: ...
def des(self, ctx: DesCtx[Domain], value: Domain, cls: type[MyType]) -> MyType: ...
```
And unloaders must implement:
```py
def can_unload(self, cls: type) -> bool: ...
def ser(self, ctx: SerCtx[Domain], obj: MyType) -> Domain: ...
```
Where `DesCtx` and `SerCtx` are used to recursively call the top level converter being used.
In most cases can_unload can be implemented in terms of can_load.
The converters implemented by this library are available in `SlySerialize.COMMON_CONVERTERS`, which is a `ConverterCollection` that implements `Converter[JsonType]`.
To use a custom converter, pass any `Converter` to `from_json` or `to_json`:
```py
# ...
custom_converter = COMMON_CONVERTERS.with_(MyConverter())
thing = from_json(MyClass[int], serialized, custom_converter)
```
It is not strictly necessary to use a `ConverterCollection`.
## Async Loaders
Async loaders are supported. They must implement the following methods:
```py
def can_load(self, cls: type) -> bool: ...
async def des(self, ctx: DesCtx[Domain], value: Domain, cls: type[MyType]) -> MyType: ...
```
When a loader is async or part of a `ConverterCollection`, then `from_json_async` must be used instead of `from_json` or an error will be raised. There is no async version of `to_json` nor any async version of `Unloader`. It is OK to pass a `ConverterCollection` with async loaders to `to_json`, since it will only access converters that implement `Loader`.
Raw data
{
"_id": null,
"home_page": "",
"name": "SlySerialize",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": "",
"keywords": "",
"author": "Dunkyl \ud83d\udd23\ud83d\udd23",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/df/5b/71eec388e303d4caf11c5dd8ed86a820fb3c4986faf4471c8268834d9faa/SlySerialize-0.0.2.tar.gz",
"platform": null,
"description": "#  Sly Serialize for Python\r\n\r\nConvert JSON-like data structures into nice Python objects.\r\n\r\nKey features:\r\n\r\n- Common, built-in types like `set`, `tuple` and `Enum`\r\n- Generic dataclasses and nested generics\r\n- Type aliases\r\n- Union types\r\n- Recursive types and delayed annotations\r\n- Custom deserialization\r\n- Asynchronous custom deserialization\r\n- Zero dependencies\r\n\r\nIn just one line:\r\n```py\r\nassert myThing == from_json(MyClass[int], to_json(myThing))\r\n```\r\n\r\n## Install\r\n\r\n```shell\r\npip install slyserialize\r\n```\r\n\r\n## Basic usage\r\n\r\nCall `from_json` with a target type, with generic arguments, and some json data, such as returned from `json.loads`. Generic arguments are optional, but if you don't provide them, you'll get a `TypeError` if the target type requires them to be concrete. See the final line in the following example:\r\n\r\n```py\r\nfrom typing import Generic, TypeVar, TypeAlias\r\nfrom dataclasses import dataclass\r\nfrom SlySerialize import from_json\r\n\r\nListOfIntegers: TypeAlias = list[int]\r\nT = TypeVar(\"T\")\r\n\r\n@dataclass\r\nclass MyClass(Generic[T]):\r\n aliased: ListOfIntegers\r\n generic: T\r\n builtin: tuple[float, list[str]]\r\n union: dict[str, T] | None\r\n delayed: 'MyClass[T] | None'\r\n\r\nmy_obj = MyClass[int]([1, 2, 3], 42, (3.1, [\"a\"]), None, None)\r\n\r\n# dataclasses.asdict(my_obj)\r\nserialized = {\r\n \"aliased\": [1, 2, 3], \"generic\": 42,\r\n \"union\": None, \"delayed\": None,\r\n \"builtin\": [3.1, [\"a\"]],\r\n}\r\n\r\nassert my_obj == from_json(MyClass[int], serialized)\r\n```\r\n\r\n## Should I use this?\r\n\r\nThe goal of this library is to handle deserialization cases for strongly typed dataclass and custom types, and to do so with as little code as possible.\r\n\r\nIf you only want fast and customizable *serialization*, but maybe not deserialization, you should use a library like [orjson](https://pypi.org/project/orjson/).\r\n\r\nIf you are using JSON-like types already, without generics or some other specific feature, there are also other libraries that will deserialize faster.\r\n\r\nThe serialization format for JSON in this library also prefers succinctness at the cost of certain edge cases related to overlapping type representations. See the [Default Representations](#default-representations) section for more details.\r\n\r\n## Notes\r\n\r\nType variables or mutually recursive types must be declared in the global scope if used in a delayed annotation.\r\n\r\n`SlySerialize.JsonType` is an alias for the return type of `json.loads` (Python standard library), it represents the native JSON types.\r\n\r\nIf the type is not supported, or if the representation was different than what was expected, a `TypeError` will be raised.\r\n\r\n`dataclasses.asdict` only supports serializing dataclasses, `JsonType`, `tuple`, and lists, tuples, or dicts of these. There will not be an error, but, the return type is *not* `JsonType`. Other types will be passed through unaffected. If you want to serialize a dataclass with a member that is not one of these types, you may want to implement your own serialization.\r\n\r\n## Default Representations\r\n\r\nThe following types are supported by default:\r\n\r\n- `NoneType`, `bool`, `int`, `float`, and `str` as themselves\r\n- `list` and `dict` as arrays and maps, with or without their generic arguments\r\n - `dict` is only supported if the key type is `str`\r\n - The default value type used with no arguments is `JsonType`\r\n- `tuple` and `set` as an array\r\n- Dataclasses as maps\r\n- Enum types of `str` or `int` as their value\r\n- Union types as whichever case the value would otherwise be represented as\r\n- Generic types are substituted for their concrete type. If the type is not available, a `ValueError` is raised.\r\n- The covariant versions of `list` and `map`, `collections.abc.Sequence` and `collections.abc.Mapping`, are also supported.\r\n - Consequently, `JsonType` is a valid type to deserialize to. If you want to do nothing to a member during deserialization, use `JsonType` as the type.\r\n\r\nNot all types are guaranteed to round-trip. For a simple example, `list` and `set` are both serialized as a JSON array, so if there is a union `list | set`, and an empty value, then the first case, `list`, will be selected during deserialization. Other examples would include `dict` and classes, or classes that have the same member names.\r\n\r\n## Serialization\r\n\r\nImplementations for serialization is provided. Some type loaders that are needed for deserialization do not have a corresponding serializer, such as `TypeVar` and `TypeAlias`, since it would be unusual for an instance value to be these types.\r\n\r\n```py\r\n# ...\r\nfrom SlySerialize import to_json\r\n\r\nassert serialized == to_json(my_obj)\r\n\r\n```\r\n\r\nIn most cases, `to_json` should be the inverse of `from_json`.\r\n\r\n## Custom Converters\r\n\r\nCustom converters can be created for any type. Subclass `Loader` or `Unloader`, or `Converter` for both. These have a generic argument for the domain type that they serialize to or from. For JSON, this is `JsonType`. Some of the loaders implemented by this library do not require a specific domain, so they may be combined with custom loaders for other domain types.\r\n\r\nLoaders must implement the following two methods:\r\n\r\n```py\r\n def can_load(self, cls: type) -> bool: ...\r\n\r\n def des(self, ctx: DesCtx[Domain], value: Domain, cls: type[MyType]) -> MyType: ...\r\n```\r\n\r\nAnd unloaders must implement:\r\n\r\n```py\r\n def can_unload(self, cls: type) -> bool: ...\r\n\r\n def ser(self, ctx: SerCtx[Domain], obj: MyType) -> Domain: ...\r\n```\r\n\r\nWhere `DesCtx` and `SerCtx` are used to recursively call the top level converter being used.\r\n\r\nIn most cases can_unload can be implemented in terms of can_load.\r\n\r\nThe converters implemented by this library are available in `SlySerialize.COMMON_CONVERTERS`, which is a `ConverterCollection` that implements `Converter[JsonType]`.\r\n\r\nTo use a custom converter, pass any `Converter` to `from_json` or `to_json`:\r\n\r\n```py\r\n# ...\r\n\r\ncustom_converter = COMMON_CONVERTERS.with_(MyConverter())\r\n\r\nthing = from_json(MyClass[int], serialized, custom_converter)\r\n```\r\n\r\nIt is not strictly necessary to use a `ConverterCollection`.\r\n\r\n## Async Loaders\r\n\r\nAsync loaders are supported. They must implement the following methods:\r\n\r\n```py\r\n def can_load(self, cls: type) -> bool: ...\r\n\r\n async def des(self, ctx: DesCtx[Domain], value: Domain, cls: type[MyType]) -> MyType: ...\r\n```\r\n\r\nWhen a loader is async or part of a `ConverterCollection`, then `from_json_async` must be used instead of `from_json` or an error will be raised. There is no async version of `to_json` nor any async version of `Unloader`. It is OK to pass a `ConverterCollection` with async loaders to `to_json`, since it will only access converters that implement `Loader`.\r\n\r\n",
"bugtrack_url": null,
"license": "MIT License Copyright (c) 2023 Maroue Reus Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
"summary": "Convert JSON-like data structures into nice Python objects.",
"version": "0.0.2",
"project_urls": {
"Bug Tracker": "https://github.com/dunkyl/SlySerialize/issues",
"Documentation": "https://docs.dunkyl.net/SlySerialize",
"Homepage": "https://github.com/dunkyl/SlySerialize",
"Repository": "https://github.com/dunkyl/SlySerialize"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b708eb0d0bf975a26f18afe497f318902882db3cec67c671232be570d140a996",
"md5": "958afbcac5196dda1b7ab43bb4ecb729",
"sha256": "a1036fdc2a3be000565acae260f6e3d5ed911ca6c1f68927a43aa9dc937eaf72"
},
"downloads": -1,
"filename": "SlySerialize-0.0.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "958afbcac5196dda1b7ab43bb4ecb729",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 11573,
"upload_time": "2023-08-12T06:34:01",
"upload_time_iso_8601": "2023-08-12T06:34:01.439953Z",
"url": "https://files.pythonhosted.org/packages/b7/08/eb0d0bf975a26f18afe497f318902882db3cec67c671232be570d140a996/SlySerialize-0.0.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "df5b71eec388e303d4caf11c5dd8ed86a820fb3c4986faf4471c8268834d9faa",
"md5": "d5d56bad64b94253ee49430fd1bdaa32",
"sha256": "3ee039618e09a60c92a37fa6af5bef3e54e16714fba913bce23bb21df7868e07"
},
"downloads": -1,
"filename": "SlySerialize-0.0.2.tar.gz",
"has_sig": false,
"md5_digest": "d5d56bad64b94253ee49430fd1bdaa32",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 15856,
"upload_time": "2023-08-12T06:34:02",
"upload_time_iso_8601": "2023-08-12T06:34:02.949420Z",
"url": "https://files.pythonhosted.org/packages/df/5b/71eec388e303d4caf11c5dd8ed86a820fb3c4986faf4471c8268834d9faa/SlySerialize-0.0.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-08-12 06:34:02",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "dunkyl",
"github_project": "SlySerialize",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "slyserialize"
}