{"id":13501340,"url":"https://github.com/monomonedula/nvelope","last_synced_at":"2025-07-18T11:07:19.880Z","repository":{"id":43686431,"uuid":"414160091","full_name":"monomonedula/nvelope","owner":"monomonedula","description":"Define your JSON schema as Python dataclasses","archived":false,"fork":false,"pushed_at":"2023-09-23T09:54:12.000Z","size":93,"stargazers_count":61,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-20T11:16:57.182Z","etag":null,"topics":["json","json-deserialization","json-schema","json-serialization","marshalling","python3","unmarshalling"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/monomonedula.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-10-06T10:06:49.000Z","updated_at":"2025-06-16T08:49:46.000Z","dependencies_parsed_at":"2024-01-16T10:36:12.145Z","dependency_job_id":"5ff1cda7-8551-4687-946a-578a45dd43ae","html_url":"https://github.com/monomonedula/nvelope","commit_stats":{"total_commits":40,"total_committers":3,"mean_commits":"13.333333333333334","dds":0.125,"last_synced_commit":"374e3170ed29a3bf6edfceb5459efc29d75a4d76"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/monomonedula/nvelope","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monomonedula%2Fnvelope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monomonedula%2Fnvelope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monomonedula%2Fnvelope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monomonedula%2Fnvelope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/monomonedula","download_url":"https://codeload.github.com/monomonedula/nvelope/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monomonedula%2Fnvelope/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265746662,"owners_count":23821706,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json","json-deserialization","json-schema","json-serialization","marshalling","python3","unmarshalling"],"created_at":"2024-07-31T22:01:33.932Z","updated_at":"2025-07-18T11:07:19.854Z","avatar_url":"https://github.com/monomonedula.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"[![codecov](https://codecov.io/gh/monomonedula/nvelope/branch/master/graph/badge.svg?token=yunFiDdUEK)](https://codecov.io/gh/monomonedula/nvelope)\n[![Build Status](https://app.travis-ci.com/monomonedula/nvelope.svg?branch=master)](https://app.travis-ci.com/monomonedula/nvelope)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![Downloads](https://pepy.tech/badge/nvelope)](https://pepy.tech/project/nvelope)\n# nvelope\n\nDefine your JSON schema as Python dataclasses\n\nIt's kinda like Pydantic but better.\n\nNow with JSON-schema generation!\n\n## Installation\n`pip install nvelope`\n\n\n## The problem it solves\n\nWith `nvelope` you can define dataclasses which know how to convert themselves from/to JSON.\nAll with custom checks and custom defined conversions from/to JSON for any type you want to put into your dataclass.\n\nThis library was designed with extensibility in mind, \nso it relies on interfaces (for the most part) rather than \nsome weird inheritance stuff and other magic.\n\nYou can (and probably should) take a look at the code! \nThe code base is microscopic compared to Pydantic.\n\n\n\n## Usage\n\nSay you have a JSON representing a user in your app looking something like this\n```json\n{\n    \"id\": 530716139,\n    \"username\": \"johndoe\",\n    \"language_code\": \"en\"\n}\n```\n\nYou define an envelope for it\n\n```python\nfrom dataclasses import dataclass\n\nfrom nvelope import (Obj, int_conv, string_conv)\n\n@dataclass      # note the @dataclass decorator, it is important\nclass User(Obj):\n    _conversion = {\n        \"id\": int_conv,\n        \"language_code\": string_conv,\n        \"username\": string_conv,\n    }\n\n    id: int\n    language_code: str\n    username: str\n\n```\n\n\nNow you have a model that knows how to read data from the JSON \n(not the raw string, actually, but to the types that are allowed by the\nstandard `json.dumps` function e.g. `dict`, `list`, `str`, `int`, `float`, `bool`, `None` ) ...\n\n```python\nuser = User.from_json(\n    {\n        \"id\": 530716139,\n        \"username\": \"johndoe\",\n        \"language_code\": \"en\"\n    }\n)\n```\n... and knows how to convert itself into JSON \n\n```python\nUser(\n    id=530716139,\n    username=\"johndoe\",\n    language_code=\"en\",\n).as_json() \n\n# returns a dictionary {\n#     \"id\": 530716139,\n#     \"username\": \"johndoe\",\n#     \"language_code\": \"en\"\n# }\n```\n\n\n### Compound envelopes\nYou can also define compound envelopes.\n\nSay we want to define a message and include info about the sender.\nHaving defined the `User` envelope, we can do it like this:\n\n```python\nfrom nvelope import CompoundConv\n\n@dataclass\nclass Message(Obj):\n    _conversion = {\n        \"message_id\": int_conv,\n        \"from_\": CompoundConv(User),\n        \"text\": string_conv,\n    }\n\n    from_: User\n    text: str\n    message_id: int\n```\nand use it the same way:\n\n```python\n# reading an obj from parsed json like this\n\nMessage.from_json(\n    {\n        \"message_id\": 44,\n        \"text\": \"hello there\",\n        \"from_\": {\n            \"id\": 530716139,\n            \"username\": \"johndoe\",\n            \"language_code\": \"en\"\n        }\n    }\n)\n\n# and dumping an object to json like this\n\nimport json\n\njson.dumps(\n    Message(\n        message_id=44,\n        text=\"whatever\",\n        from_=User(\n            id=530716139,\n            username=\"johndoe\",\n            language_code=\"en\",\n        )\n    ).as_json()\n)\n```\n\n\n### Arrays\n\nThis is how you define arrays:\n\n```python\nfrom nvelope import Arr, CompoundConv\n\n\nclass Users(Arr):\n    conversion = CompoundConv(User)\n\n\n# Same API inherited from nvelope.Compound interface\n\nUsers.from_json(\n    [\n        {\n            \"id\": 530716139,\n            \"username\": \"johndoe\",\n            \"language_code\": \"en\",\n        },\n        {\n            \"id\": 452341341,\n            \"username\": \"ivandrago\",\n            \"language_code\": \"ru\",\n        }\n    ]\n)\n\nUsers(\n    [\n        User(\n            id=530716139,\n            username=\"johndoe\",\n            language_code=\"en\",\n        ),\n        User(\n            id=452341341,\n            username=\"ivandrago\",\n            language_code=\"ru\",\n        ),\n    ]\n).as_json()\n```\n\n### Field aliases\n\nAt some point you may need to define an envelope for an API containing certain field names which cannot be\nused in python since they are reserved keywords (such as `def`, `from`, etc.).\n\nThere's a solution for this:\n\n```python\nfrom dataclasses import dataclass\nfrom nvelope import Obj, string_conv, CompoundConv, AliasTable\n\n@dataclass\nclass Comment(Obj):\n    _conversion = {\n        \"text\": string_conv,\n        \"from_\": CompoundConv(User),\n    }\n    \n    \n    _alias_table = AliasTable({\"from_\": \"from\"})\n            \n    text: str\n    from_: User\n\n```\n\nIn this case `from` key gets replaced by `from_` in the python model. \nThe `from_` field gets translated back to `from` when calling `.as_json()`\n\n### Missing and optional fields\n\nThere's a difference between fields that can be set to `None` and fields which may be missing in the JSON at all.\n\nThis is how you specify that a some field may be missing from the JSON and that's OK:\n```python\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom nvelope import MaybeMissing, Obj, OptionalConv, AliasTable\n\n@dataclass\nclass Comment(Obj):\n    _alias_table = AliasTable(\n        {\"from_\": \"from\"}\n    )\n    \n    text: str\n    img: Optional[str]          # this field can be set to None (null), but is must always be present in the JSON\n    from_: MaybeMissing[User]   # this field can be missing from JSON body\n\n    _conversion = {\n        \"text\": string_conv,\n        \"img\": OptionalConv(string_conv),   # note the wrapping with OptionalConv\n        \"from_\": CompoundConv(User),\n    }\n\n```\n\nThis is how you check if the `MaybeMissing` field is actually missing\n```python\ncomment.from_.has()     # returns False if the field is missing\n```\n\nand this is how you get the value:\n```python\ncomment.value()     # raises an error if there's no value, \n                    # so it is recommended to check the output of .has()\n                    #  before calling .value() \n```\n\n### Json-schema support\nThe `Comment` model from we have defined generates schema like this:\n```python\n    Comment.schema()\n```\n\nwith the returned schema looking like this:\n```python\n{\n    \"type\": \"object\",\n    \"properties\": {\n        \"from\": {\n            \"properties\": {\n                \"id\": {\"type\": \"integer\"},\n                \"language_code\": {\"type\": \"string\"},\n                \"username\": {\"type\": \"string\"},\n            },\n            \"required\": [\"id\", \"language_code\", \"username\"],\n            \"type\": \"object\",\n        },\n        \"img\": {\"type\": [\"string\", \"null\"]},\n        \"text\": {\"type\": \"string\"},\n    },\n    \"required\": [\"text\", \"img\"],\n}\n```\n**NOTE**: `nvelope` does not perform json schema checks.\n\n### Custom conversions\n\n\nYou may define a custom conversions inheriting from `nvelope.nvelope.Conversion` abstract base class \nor using `nvelope.nvelope.ConversionOf` class. \n\nFor example, this is how `datetime_iso_format_conv` is defined:\n\n```python\nfrom nvelope import WithTypeCheckOnDump, ConversionOf\n\ndatetime_iso_format_conv = WithTypeCheckOnDump(\n    datetime.datetime,\n    ConversionOf(\n        to_json=lambda v: v.isoformat(),\n        from_json=lambda s: datetime.datetime.fromisoformat(s),\n    ),\n)\n\n```\n\nSay we want to jsonify a `datetime` field as POSIX timestamp, instead of storing it in ISO string format.\n\n```python\ndatetime_timestamp_conv = ConversionOf(\n    to_json=lambda v: v.timestamp(),\n    from_json=lambda s: datetime.datetime.fromtimestamp(s),\n    schema={\"type\": \"number\"},\n)\n```\n\nWe could also add `WithTypeCheckOnDump` wrapper in order to add explicit check that \nthe value passed into `.from_json()`\nis indeed `float`.\n\n```python\nfrom nvelope import ConversionOf\n\ndatetime_timestamp_conv = WithTypeCheckOnDump(\n    float,\n    ConversionOf(\n        to_json=lambda v: v.timestamp(),\n        from_json=lambda s: datetime.datetime.fromtimestamp(s),\n        schema={\"type\": \"number\"},\n    )\n)\n```\n\nYou may also go further and implement custom conversion.\nInherit from `nvelope.Conversion` interface, implement its abstract methods, and you are good to go.\n\n\n### Custom compounds\n\nYou can also define custom alternatives to `nvelope.Obj` and `nvelope.Arr`.\nIt will work fine as long as they inherit `nvelope.Compound` interface.\n\nIt currently required 3 methods:\n- `from_json` \n- `as_json`\n- `schema`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonomonedula%2Fnvelope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmonomonedula%2Fnvelope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonomonedula%2Fnvelope/lists"}