# jetblack-serialization
Serialization for JSON, YAML and XML in Python using type annotations
(read the [docs](https://rob-blackbourn.github.io/jetblack-serialization/)).
## Installation
This is a Python 3.12+ package.
The package can be installed with pip.
```bash
pip install jetblack-serialization
```
By default, the dependencies for YAML and XML are not installed.
To install the dependencies for XML
([`lxml`](https://lxml.de/)).
```bash
pip install jetblack-serialization[xml]
```
To install the dependencies for YAML ([`PyYAML`](https://github.com/yaml/pyyaml)).
```bash
pip install jetblack-serialization[yaml]
```
To install the dependencies for all.
```bash
pip install jetblack-serialization[xml,yaml]
```
## Overview
The package adds support for type annotations when serializing or deserializing
JSON, YAML or XML.
### JSON
Given a typed dictionary:
```python
from datetime import datetime
from typing import Optional, TypedDict, Union
class Book(TypedDict, total=False):
book_id: int
title: str
author: str
publication_date: datetime
keywords: list[str]
phrases: list[str]
age: Optional[Union[datetime, int]]
pages: Optional[int]
```
#### Serializing JSON
This could be serialized to JSON as:
```python
from stringcase import camelcase
from jetblack_serialization.json import serialize, SerializerConfig
obj: Book = {
'author': 'Chairman Mao',
'book_id': 42,
'title': 'Little Red Book',
'publication_date': datetime(1973, 1, 1, 21, 52, 13),
'keywords': ['Revolution', 'Communism'],
'phrases': [
'Revolutionary wars are inevitable in class society',
'War is the continuation of politics'
],
'age': 24,
}
text = serialize(
obj,
Book,
SerializerConfig(key_serializer=camelcase, pretty_print=True)
)
print(text)
```
giving:
```json
{
"bookId": 42,
"title": "Little Red Book",
"author": "Chairman Mao",
"publicationDate": "1973-01-01T21:52:13.00Z",
"keywords": ["Revolution", "Communism"],
"phrases": ["Revolutionary wars are inevitable in class society", "War is the continuation of politics"],
"age": 24,
"pages": null
}
```
Note the fields have been camel cased, and the publication date has been turned
into an ISO 8601 date.
#### Deserializing JSON
We can deserialize the data as follows:
```python
from stringcase import snakecase
from jetblack_serialization.json import deserialize, SerializerConfig
dct = deserialize(
text,
Annotated[Book, JSONValue()],
SerializerConfig(key_deserializer=snakecase)
)
```
### YAML
YAML is a superset of JSON, so for serialization things are very similar.
Given a typed dictionary:
```python
from datetime import datetime
from typing import Optional, TypedDict, Union
class Book(TypedDict, total=False):
book_id: int
title: str
author: str
publication_date: datetime
keywords: list[str]
phrases: list[str]
age: Optional[Union[datetime, int]]
pages: Optional[int]
```
#### Serializing YAML
This could be serialized to YAML as:
```python
from stringcase import camelcase
from jetblack_serialization.yaml import serialize, SerializerConfig
obj: Book = {
'author': 'Chairman Mao',
'book_id': 42,
'title': 'Little Red Book',
'publication_date': datetime(1973, 1, 1, 21, 52, 13),
'keywords': ['Revolution', 'Communism'],
'phrases': [
'Revolutionary wars are inevitable in class society',
'War is the continuation of politics'
],
'age': 24,
}
text = serialize(
obj,
Book,
SerializerConfig(key_serializer=camelcase, pretty_print=True)
)
print(text)
```
giving:
```yaml
bookId: 42
title: Little Red Book
author: Chairman Mao
publicationDate: '1973-01-01T21:52:13.00Z'
keywords:
- Revolution
- Communism
phrases:
- Revolutionary wars are inevitable in class society
- War is the continuation of politics
age: 24
pages: null
```
Note the fields have been camel cased, and the publication date has been turned
into an ISO 8601 date.
#### Deserializing YAML
We can deserialize the data as follows:
```python
from stringcase import snakecase
from jetblack_serialization.yaml import deserialize, SerializerConfig
dct = deserialize(
text,
Annotated[Book, JSONValue()],
SerializerConfig(key_deserializer=snakecase)
)
```
### XML
The XML version of the typed dictionary might look like this:
```python
from datetime import datetime
from typing import Optional, TypedDict, Union
from typing_extensions import Annotated
from jetblack_serialization.xml import XMLEntity, XMLAttribute
class Book(TypedDict, total=False):
book_id: Annotated[int, XMLAttribute("bookId")]
title: str
author: str
publication_date: datetime
keywords: Annotated[list[Annotated[str, XMLEntity("Keyword")]], XMLEntity("Keywords")]
phrases: list[str]
age: Optional[Union[datetime, int]]
pages: Optional[int]
```
Note we have introduced some annotations to control the serialization.
For XML we have used pascal-case to serialized the keys and snake-case
for deserialization.
#### Serializing XML
To serialize we need to provide the containing tag `Book`:
```python
from stringcase import pascalcase
from jetblack_serialization.xml import serialize, SerializerConfig
book: Book = {
'author': 'Chairman Mao',
'book_id': 42,
'title': 'Little Red Book',
'publication_date': datetime(1973, 1, 1, 21, 52, 13),
'keywords': ['Revolution', 'Communism'],
'phrases': [
'Revolutionary wars are inevitable in class society',
'War is the continuation of politics'
],
'age': 24,
'pages': None
}
text = serialize(
book,
Annotated[Book, XMLEntity("Book")],
SerializerConfig(key_serializer=pascalcase)
)
print(text)
```
Producing:
```xml
<Book bookId="42">
<Title>Little Red Book</Title>
<Author>Chairman Mao</Author>
<PublicationDate>1973-01-01T21:52:13.00Z</PublicationDate>
<Keywords>
<Keyword>Revolution</Keyword>
<Keyword>Communism</Keyword>
</Keywords>
<Phrase>Revolutionary wars are inevitable in class society</Phrase>
<Phrase>War is the continuation of politics</Phrase>
<Age>24</Age>
</Book>'
```
The annotations are more elaborate here. However, much of the typed dictionary
requires no annotation.
First we needed the outer document wrapper `XMLEntity("Book")`.
Next we annotated the `book_id` to be an `XMLAttribute`.
Finally we annotated the two lists differently. The `keywords` list used
a nested structure, which we indicated by giving the list a different
`XMLEntity` tag to the list items. For the phrases we used the default
in-line behaviour.
#### Deserializing XML
We can deserialize the XML as follows:
```python
from stringcase import snakecase
from jetblack_serialization.xml import deserialize, SerializerConfig
dct = deserialize(
text,
Annotated[Book, XMLEntity("Book")],
SerializerConfig(key_deserializer=snakecase)
)
```
## Attributes
For JSON, attributes are typically not required. However
`JSONProperty(tag: str)` and `JSONValue()` are provided for
completeness.
## Contributing
To run the tests with tox and pyenv:
```bash
VIRTUALENV_DISCOVERY=pyenv tox
```
Raw data
{
"_id": null,
"home_page": null,
"name": "jetblack-serialization",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "serialization, JSON, XML, YAML",
"author": null,
"author_email": "Rob Blackbourn <rob.blackbourn@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/ae/f3/23cc256901d26cbd00421285cb6f15ab5b6313c59e3200e3c57a70b541b6/jetblack_serialization-4.0.10.tar.gz",
"platform": null,
"description": "# jetblack-serialization\n\nSerialization for JSON, YAML and XML in Python using type annotations\n(read the [docs](https://rob-blackbourn.github.io/jetblack-serialization/)).\n\n## Installation\n\nThis is a Python 3.12+ package.\n\nThe package can be installed with pip.\n\n```bash\npip install jetblack-serialization\n```\n\nBy default, the dependencies for YAML and XML are not installed.\n\nTo install the dependencies for XML\n([`lxml`](https://lxml.de/)).\n\n```bash\npip install jetblack-serialization[xml]\n```\n\nTo install the dependencies for YAML ([`PyYAML`](https://github.com/yaml/pyyaml)).\n\n```bash\npip install jetblack-serialization[yaml]\n```\n\nTo install the dependencies for all.\n\n```bash\npip install jetblack-serialization[xml,yaml]\n```\n\n## Overview\n\nThe package adds support for type annotations when serializing or deserializing\nJSON, YAML or XML.\n\n### JSON\n\nGiven a typed dictionary:\n\n```python\nfrom datetime import datetime\nfrom typing import Optional, TypedDict, Union\n\nclass Book(TypedDict, total=False):\n book_id: int\n title: str\n author: str\n publication_date: datetime\n keywords: list[str]\n phrases: list[str]\n age: Optional[Union[datetime, int]]\n pages: Optional[int]\n```\n\n#### Serializing JSON\n\nThis could be serialized to JSON as:\n\n```python\nfrom stringcase import camelcase\nfrom jetblack_serialization.json import serialize, SerializerConfig\n\nobj: Book = {\n 'author': 'Chairman Mao',\n 'book_id': 42,\n 'title': 'Little Red Book',\n 'publication_date': datetime(1973, 1, 1, 21, 52, 13),\n 'keywords': ['Revolution', 'Communism'],\n 'phrases': [\n 'Revolutionary wars are inevitable in class society',\n 'War is the continuation of politics'\n ],\n 'age': 24,\n}\ntext = serialize(\n obj,\n Book,\n SerializerConfig(key_serializer=camelcase, pretty_print=True)\n)\nprint(text)\n```\n\ngiving:\n\n```json\n{\n \"bookId\": 42,\n \"title\": \"Little Red Book\",\n \"author\": \"Chairman Mao\",\n \"publicationDate\": \"1973-01-01T21:52:13.00Z\",\n \"keywords\": [\"Revolution\", \"Communism\"],\n \"phrases\": [\"Revolutionary wars are inevitable in class society\", \"War is the continuation of politics\"],\n \"age\": 24,\n \"pages\": null\n}\n```\n\nNote the fields have been camel cased, and the publication date has been turned\ninto an ISO 8601 date.\n\n#### Deserializing JSON\n\nWe can deserialize the data as follows:\n\n```python\nfrom stringcase import snakecase\nfrom jetblack_serialization.json import deserialize, SerializerConfig\n\ndct = deserialize(\n text,\n Annotated[Book, JSONValue()],\n SerializerConfig(key_deserializer=snakecase)\n)\n```\n\n### YAML\n\nYAML is a superset of JSON, so for serialization things are very similar.\n\nGiven a typed dictionary:\n\n```python\nfrom datetime import datetime\nfrom typing import Optional, TypedDict, Union\n\nclass Book(TypedDict, total=False):\n book_id: int\n title: str\n author: str\n publication_date: datetime\n keywords: list[str]\n phrases: list[str]\n age: Optional[Union[datetime, int]]\n pages: Optional[int]\n```\n\n#### Serializing YAML\n\nThis could be serialized to YAML as:\n\n```python\nfrom stringcase import camelcase\nfrom jetblack_serialization.yaml import serialize, SerializerConfig\n\nobj: Book = {\n 'author': 'Chairman Mao',\n 'book_id': 42,\n 'title': 'Little Red Book',\n 'publication_date': datetime(1973, 1, 1, 21, 52, 13),\n 'keywords': ['Revolution', 'Communism'],\n 'phrases': [\n 'Revolutionary wars are inevitable in class society',\n 'War is the continuation of politics'\n ],\n 'age': 24,\n}\ntext = serialize(\n obj,\n Book,\n SerializerConfig(key_serializer=camelcase, pretty_print=True)\n)\nprint(text)\n```\n\ngiving:\n\n```yaml\nbookId: 42\ntitle: Little Red Book\nauthor: Chairman Mao\npublicationDate: '1973-01-01T21:52:13.00Z'\nkeywords:\n- Revolution\n- Communism\nphrases:\n- Revolutionary wars are inevitable in class society\n- War is the continuation of politics\nage: 24\npages: null\n```\n\nNote the fields have been camel cased, and the publication date has been turned\ninto an ISO 8601 date.\n\n#### Deserializing YAML\n\nWe can deserialize the data as follows:\n\n```python\nfrom stringcase import snakecase\nfrom jetblack_serialization.yaml import deserialize, SerializerConfig\n\ndct = deserialize(\n text,\n Annotated[Book, JSONValue()],\n SerializerConfig(key_deserializer=snakecase)\n)\n```\n\n### XML\n\nThe XML version of the typed dictionary might look like this:\n\n```python\nfrom datetime import datetime\nfrom typing import Optional, TypedDict, Union\nfrom typing_extensions import Annotated\nfrom jetblack_serialization.xml import XMLEntity, XMLAttribute\n\nclass Book(TypedDict, total=False):\n book_id: Annotated[int, XMLAttribute(\"bookId\")]\n title: str\n author: str\n publication_date: datetime\n keywords: Annotated[list[Annotated[str, XMLEntity(\"Keyword\")]], XMLEntity(\"Keywords\")]\n phrases: list[str]\n age: Optional[Union[datetime, int]]\n pages: Optional[int]\n```\n\nNote we have introduced some annotations to control the serialization.\nFor XML we have used pascal-case to serialized the keys and snake-case\nfor deserialization.\n\n#### Serializing XML\n\nTo serialize we need to provide the containing tag `Book`:\n\n```python\nfrom stringcase import pascalcase\nfrom jetblack_serialization.xml import serialize, SerializerConfig\n\nbook: Book = {\n 'author': 'Chairman Mao',\n 'book_id': 42,\n 'title': 'Little Red Book',\n 'publication_date': datetime(1973, 1, 1, 21, 52, 13),\n 'keywords': ['Revolution', 'Communism'],\n 'phrases': [\n 'Revolutionary wars are inevitable in class society',\n 'War is the continuation of politics'\n ],\n 'age': 24,\n 'pages': None\n}\ntext = serialize(\n book,\n Annotated[Book, XMLEntity(\"Book\")],\n SerializerConfig(key_serializer=pascalcase)\n)\nprint(text)\n```\n\nProducing:\n\n```xml\n<Book bookId=\"42\">\n <Title>Little Red Book</Title>\n <Author>Chairman Mao</Author>\n <PublicationDate>1973-01-01T21:52:13.00Z</PublicationDate>\n <Keywords>\n <Keyword>Revolution</Keyword>\n <Keyword>Communism</Keyword>\n </Keywords>\n <Phrase>Revolutionary wars are inevitable in class society</Phrase>\n <Phrase>War is the continuation of politics</Phrase>\n <Age>24</Age>\n</Book>'\n```\n\nThe annotations are more elaborate here. However, much of the typed dictionary\nrequires no annotation.\n\nFirst we needed the outer document wrapper `XMLEntity(\"Book\")`.\n\nNext we annotated the `book_id` to be an `XMLAttribute`.\n\nFinally we annotated the two lists differently. The `keywords` list used\na nested structure, which we indicated by giving the list a different\n`XMLEntity` tag to the list items. For the phrases we used the default\nin-line behaviour.\n\n#### Deserializing XML\n\nWe can deserialize the XML as follows:\n\n```python\nfrom stringcase import snakecase\nfrom jetblack_serialization.xml import deserialize, SerializerConfig\n\ndct = deserialize(\n text,\n Annotated[Book, XMLEntity(\"Book\")],\n SerializerConfig(key_deserializer=snakecase)\n)\n```\n\n## Attributes\n\nFor JSON, attributes are typically not required. However\n`JSONProperty(tag: str)` and `JSONValue()` are provided for\ncompleteness.\n\n## Contributing\n\nTo run the tests with tox and pyenv:\n\n```bash\nVIRTUALENV_DISCOVERY=pyenv tox\n```\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "Serialization for JSON and XML using typing",
"version": "4.0.10",
"project_urls": {
"Homepage": "https://github.com/rob-blackbourn/jetblack-serialization",
"Issues": "https://github.com/rob-blackbourn/jetblack-serialization/issues"
},
"split_keywords": [
"serialization",
" json",
" xml",
" yaml"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "5093c9e530efdc9f8ab9b5e030d6d5b38a5e073ea838041820e52262df6c1940",
"md5": "e042f03bba14a9fcaeb827e88b893045",
"sha256": "8bdba9a71d4b3300eebcc2ac30fe54fe875c19c7b545aa5d97cd851534d3bc84"
},
"downloads": -1,
"filename": "jetblack_serialization-4.0.10-py3-none-any.whl",
"has_sig": false,
"md5_digest": "e042f03bba14a9fcaeb827e88b893045",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 32273,
"upload_time": "2025-09-09T06:37:37",
"upload_time_iso_8601": "2025-09-09T06:37:37.258285Z",
"url": "https://files.pythonhosted.org/packages/50/93/c9e530efdc9f8ab9b5e030d6d5b38a5e073ea838041820e52262df6c1940/jetblack_serialization-4.0.10-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "aef323cc256901d26cbd00421285cb6f15ab5b6313c59e3200e3c57a70b541b6",
"md5": "7d8a2a615529c7b6bcd466e2439e6392",
"sha256": "97b6f1576f756460f06e4c5db467673943a90f467450d4ac5ab188921d6a5d53"
},
"downloads": -1,
"filename": "jetblack_serialization-4.0.10.tar.gz",
"has_sig": false,
"md5_digest": "7d8a2a615529c7b6bcd466e2439e6392",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 24797,
"upload_time": "2025-09-09T06:37:38",
"upload_time_iso_8601": "2025-09-09T06:37:38.862608Z",
"url": "https://files.pythonhosted.org/packages/ae/f3/23cc256901d26cbd00421285cb6f15ab5b6313c59e3200e3c57a70b541b6/jetblack_serialization-4.0.10.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-09 06:37:38",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "rob-blackbourn",
"github_project": "jetblack-serialization",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "jetblack-serialization"
}