dataclass-jsonable


Namedataclass-jsonable JSON
Version 0.1.3 PyPI version JSON
download
home_pagehttps://github.com/hit9/dataclass-jsonable
SummarySimple and flexible conversions between dataclasses and jsonable dictionaries.
upload_time2023-09-06 06:11:08
maintainer
docs_urlNone
authorhit9
requires_python>=3.7
licenseBSD
keywords dataclasses json jsonable
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # dataclass-jsonable

[![dataclass-jsonable ci](https://github.com/hit9/dataclass-jsonable/actions/workflows/ci.yml/badge.svg)](https://github.com/hit9/dataclass-jsonable/actions/workflows/ci.yml)
![](https://img.shields.io/badge/license-BSD3-brightgreen)

[中文说明](README.zh.md)

Simple and flexible conversions between dataclasses and jsonable dictionaries.

It maps dataclasses to jsonable dictionaries but not json strings.


## Features

* Easy to use.
* Supports common type annotations.
* Supports recursive conversions.
* Supports field-level and dataclass-level overriding.

## Installation

Requirements: Python >= 3.7

Install via `pip`:

```
pip install dataclass-jsonable
```

## Quick Example

```python
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal
from enum import IntEnum
from typing import List
from dataclass_jsonable import J

class Color(IntEnum):
    BLACK = 0
    BLUE = 1
    RED = 2

@dataclass
class Pen(J):
    color: Color
    price: Decimal
    produced_at: datetime

@dataclass
class Box(J):
    pens: List[Pen]

box = Box(pens=[Pen(color=Color.BLUE, price=Decimal("20.1"), produced_at=datetime.now())])

# Encode to a jsonable dictionary.
d = box.json()
print(d)  # {'pens': [{'color': 1, 'price': '20.1', 'produced_at': 1660023062}]}

# Construct dataclass from a jsonable dictionary.
print(Box.from_json(d))
```

APIs are only the two: `.json()` and `.from_json()`.

## Built-in Supported Types

* `bool`, `int`, `float`, `str`, `None` encoded as it is.

  ```python
  @dataclass
  class Obj(J):
      a: int
      b: str
      c: bool
      d: None

  Obj(a=1, b="b", c=True, d=None).json()
  # => {'a': 1, 'b': 'b', 'c': True, 'd': None}
  ```

* `Decimal` encoded to `str`.

  ```python
  @dataclass
  class Obj(J):
      a: Decimal

  Obj(a=Decimal("3.1")).json()  # => {'a': '3.1'}
  ```

* `datetime` encoded to timestamp integer via `.timestamp()` method.
  `timedelta` encoded to integer via `.total_seconds()` method.

  ```python
  @dataclass
  class Obj(J):
      a: datetime
      b: timedelta

  Obj(a=datetime.now(), b=timedelta(minutes=1)).json()
  # => {'a': 1660062019, 'b': 60}
  ```

* `Enum` and `IntEnum` encoded to their values via `.value` attribute.

  ```python
  @dataclass
  class Obj(J):
      status: Status

  Obj(status=Status.DONE).json()  # => {'status': 1}
  ```

* `Any` is encoded according to its `type`.

  ```python
  @dataclass
  class Obj(J):
      a: Any

  Obj(1).json()  # {'a': 1}
  Obj("a").json()  # {'a': 'a'}
  Obj.from_json({"a": 1})  # Obj(a=1)
  ```

* `Optional[X]` is supported, but `Union[X, Y, ...]` is not.

  ```python
  @dataclass
  class Obj(J):
      a: Optional[int] = None

  Obj(a=1).json()  # => {'a': 1}
  ```

* `List[X]`, `Tuple[X]`, `Set[X]` are all encoded to `list`.

  ```python
  @dataclass
  class Obj(J):
      a: List[int]
      b: Set[int]
      c: Tuple[int, str]
      d: Tuple[int, ...]

  Obj(a=[1], b={2, 3}, c=(4, "5"), d=(7, 8, 9)).json())
  # => {'a': [1], 'b': [2, 3], 'c': [4, '5'], 'd': [7, 8, 9]}

  Obj.from_json({"a": [1], "b": [2, 3], "c": [4, "5"], "d": [7, 8, 9]}))
  # => Obj(a=[1], b={2, 3}, c=(4, '5'), d=(7, 8, 9))
  ```

* `Dict[str, X]` encoded to `dict`.

  ```python
  @dataclass
  class Obj(J):
      a: Dict[str, int]
  Obj(a={"x": 1}).json()  # => {'a': {'x': 1}}
  Obj.from_json({"a": {"x": 1}}) # => Obj(a={'x': 1})
  ```

* Nested or recursively `JSONAble` (or `J`) dataclasses.

  ```python
  @dataclass
  class Elem(J):
      k: str

  @dataclass
  class Obj(J):
      a: List[Elem]

  Obj([Elem("v")]).json()  # => {'a': [{'k': 'v'}]}
  Obj.from_json({"a": [{"k": "v"}]})  # Obj(a=[Elem(k='v')])
  ```

* Postponed annotations (the `ForwardRef` in [PEP 563](https://www.python.org/dev/peps/pep-0563/)).

  ```python
  @dataclass
  class Node(J):
      name: str
      left: Optional["Node"] = None
      right: Optional["Node"] = None

  root = Node("root", left=Node("left"), right=Node("right"))
  root.json()
  # {'name': 'root', 'left': {'name': 'left', 'left': None, 'right': None}, 'right': {'name': 'right', 'left': None, 'right': None}}
  ```

If these built-in default conversion behaviors do not meet your needs,
or your type is not on the list,
you can use [json_options](#customization--overriding-examples) introduced below to customize it.

## Customization / Overriding Examples

We can override the default conversion behaviors with `json_options`,
which uses the dataclass field's metadata for field-level customization purpose,
and the namespace is `j`.

The following pseudo code gives the pattern:

```python
from dataclasses import field
from dataclass_jsonable import json_options

@dataclass
class Struct(J):
    attr: T = field(metadata={"j": json_options(**kwds)})
```

An example list about `json_options`:

* Specific a custom dictionary key over the default field's name:

   ```python
   @dataclass
   class Person(J):
       attr: str = field(metadata={"j": json_options(name="new_attr")})
   Person(attr="value").json() # => {"new_attr": "value"}
   ```

  And more, we can use a function to specific a custom dictionary key.
  This may be convenient to work with class-level `__default_json_options__` attribute (check it below).

  ```python
  @dataclass
  class Obj(J):
      simple_value: int = field(metadata={"j": json_options(name_converter=to_camel_case)})
  Obj(simple_value=1).json()  # => {"simpleValue": 1}
  ```

  And we may specific a custom field name converter when converts dictionary to dataclass:

  ```python
  @dataclass
  def Person(J):
    name: str = field(
          metadata={
              "j": json_options(
                  name_converter=lambda x: x.capitalize(),
                  name_inverter=lambda x: "nickname",
            )
        }
    )
  ```

  As the `Person` defined above, it will convert to dictionary like `{"Name": "Jack"}` and can be loaded from `{"nickname": "Jack"}`.

* Omit a field if its value is empty:

   ```python
   @dataclass
   class Book(J):
       name: str = field(metadata={"j": json_options(omitempty=True)})
   Book(name="").json() # => {}
   ```

  Further, we can specify what is 'empty' via option `omitempty_tester`:

   ```python
   @dataclass
   class Book(J):
       attr: Optional[str] = field(
           default=None,
           metadata={
               # By default, we test `empty` using `not x`.
               "j": json_options(omitempty=True, omitempty_tester=lambda x: x is None)
           },
       )

   Book(attr="").json()  # => {'attr': ''}
   Book(attr=None).json()  # => {}
   ```

* Always skip a field. So we can stop some "private" fields from exporting:

   ```python
   @dataclass
   class Obj(J):
       attr: str = field(metadata={"j": json_options(skip=True)})

   Obj(attr="private").json() # => {}
   ```

* Always keep a field without encoding nor decoding, this prevents the default encoding/decoding behavior:

   ```python
   @dataclass
   class Obj(J):
       timestamp: datetime = field(metadata={"j": json_options(keep=True)})

   Obj(timestamp=datetime.now()).json() # =>  {'timestamp': datetime.datetime(2023, 9, 5, 14, 54, 24, 679103)}
   ```

* dataclasses's `field` allows us to pass a `default` or `default_factory` argument to
  set a default value:

  ```python
  @dataclass
  class Obj(J):
      attr: List[str] = field(default_factory=list, metadata={"j": json_options(**kwds)})
  ```

  There's also an option `default_before_decoding` in dataclass-jsonable,
  which specifics a default value before decoding if the key is missing in the dictionary.
  Sometimes this way is more concise:

  ```python
  @dataclass
  class Obj(J):
      updated_at: datetime = field(metadata={"j": json_options(default_before_decoding=0)})

  Obj.from_json({})  # => Obj(updated_at=datetime.datetime(1970, 1, 1, 8, 0))
  ```

  dataclass-jsonable also introduces a class-level similar option `__default_factory__`.
  If a field has no `default` or `default_factory` declared, and has no `default_before_decoding` option used,
  this function will generate a default value according to its type, to prevent a
  "missing positional arguments" TypeError from raising.

  ```python
  from dataclass_jsonable import J, zero

  @dataclass
  class Obj(J):
      __default_factory__ = zero

      n: int
      s: str
      k: List[str]

  Obj.from_json({})  # => Obj(n=0, s='', k=[])
  ```

* Override the default encoders and decoders.

  This way, you have complete control over how to encode and decode at field level.

  ```python
  @dataclass
  class Obj(J):
      elems: List[str] = field(
          metadata={
              "j": json_options(
                  encoder=lambda x: ",".join(x),
                  decoder=lambda x: x.split(","),
              )
          }
      )

  Obj(elems=["a", "b", "c"]).json()  # => {'elems': 'a,b,c'}
  Obj.from_json({"elems": "a,b,c"})  # => Obj(elems=['a', 'b', 'c'])
  ```

  The following code snippet about `datetime` is a very common example,
  you might want ISO format datetime conversion over timestamp integers.

  ```python
  @dataclass
  class Record(J):
      created_at: datetime = field(
          default_factory=datetime.now,
          metadata={
              "j": json_options(
                  encoder=datetime.isoformat,
                  decoder=datetime.fromisoformat,
              )
          },
      )

  Record().json()  # => {'created_at': '2022-08-09T23:23:02.543007'}
  ```

  dataclass-jsonable gives `encoder` and `decoder` better alias names since 0.1.1:
  `to_json` and `from_json`.

  ```python
  @dataclass
  class Obj(J):
      elems: List[str] = field(
          metadata={
              "j": json_options(
                  to_json=lambda x: ",".join(x),  # Alias for encoder
                  from_json=lambda x: x.split(","),  # Alias for decoder
              )
          }
      )

  Obj(elems=["a", "b", "c"]).json()  # => {'elems': 'a,b,c'}
  Obj.from_json({"elems": "a,b,c"})  # => Obj(elems=['a', 'b', 'c'])
  ```

* For some very narrow scenarios, we may need to execute a hook function before decoding,
  for example, the data to be decoded is a serialized json string,
  and but we still want to use the built-in decoder functions instead of making a new decoder.

  ```python
  import json

  @dataclass
  class Obj(J):
      data: Dict[str, Any] = field(metadata={"j": json_options(before_decoder=json.loads)})

  Obj.from_json({"data": '{"k": "v"}'})
  # => Obj(data={'k': 'v'})
  ```

* Customize default behaviors at the class level.

  If an option is not explicitly set at the field level,
  the `__default_json_options__` provided at the class level will be attempted.

  ````python
  @dataclass
  class Obj(J):
      __default_json_options__ = json_options(omitempty=True)

      a: Optional[int] = None
      b: Optional[str] = None

  Obj(b="b").json() # => {'b': 'b'}
  ````

  ```python
  @dataclass
  class Obj(J):
      __default_json_options__ = json_options(name_converter=to_camel_case)

      status_code: int
      simple_value: str

  Obj2(status_code=1, simple_value="simple").json()
  # => {"statusCode": 1, "simpleValue": "simple"}
  ```

## Debuging

It provides a method `obj._get_origin_json()`,
it returns the original json dictionary which constructs instance `obj` via `from_json()`.

```python
d = {"a": 1}
obj = Obj.from_json(d)
obj._get_origin_json()
# => {"a": 1}
```

## License

BSD.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/hit9/dataclass-jsonable",
    "name": "dataclass-jsonable",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7",
    "maintainer_email": "",
    "keywords": "dataclasses,json,jsonable",
    "author": "hit9",
    "author_email": "hit9@icloud.com",
    "download_url": "https://files.pythonhosted.org/packages/91/13/ebace88a4f9cdead6e8489641ac188242a55da0e8673d877e41ec92809c3/dataclass-jsonable-0.1.3.tar.gz",
    "platform": null,
    "description": "# dataclass-jsonable\n\n[![dataclass-jsonable ci](https://github.com/hit9/dataclass-jsonable/actions/workflows/ci.yml/badge.svg)](https://github.com/hit9/dataclass-jsonable/actions/workflows/ci.yml)\n![](https://img.shields.io/badge/license-BSD3-brightgreen)\n\n[\u4e2d\u6587\u8bf4\u660e](README.zh.md)\n\nSimple and flexible conversions between dataclasses and jsonable dictionaries.\n\nIt maps dataclasses to jsonable dictionaries but not json strings.\n\n\n## Features\n\n* Easy to use.\n* Supports common type annotations.\n* Supports recursive conversions.\n* Supports field-level and dataclass-level overriding.\n\n## Installation\n\nRequirements: Python >= 3.7\n\nInstall via `pip`:\n\n```\npip install dataclass-jsonable\n```\n\n## Quick Example\n\n```python\nfrom dataclasses import dataclass\nfrom datetime import datetime\nfrom decimal import Decimal\nfrom enum import IntEnum\nfrom typing import List\nfrom dataclass_jsonable import J\n\nclass Color(IntEnum):\n    BLACK = 0\n    BLUE = 1\n    RED = 2\n\n@dataclass\nclass Pen(J):\n    color: Color\n    price: Decimal\n    produced_at: datetime\n\n@dataclass\nclass Box(J):\n    pens: List[Pen]\n\nbox = Box(pens=[Pen(color=Color.BLUE, price=Decimal(\"20.1\"), produced_at=datetime.now())])\n\n# Encode to a jsonable dictionary.\nd = box.json()\nprint(d)  # {'pens': [{'color': 1, 'price': '20.1', 'produced_at': 1660023062}]}\n\n# Construct dataclass from a jsonable dictionary.\nprint(Box.from_json(d))\n```\n\nAPIs are only the two: `.json()` and `.from_json()`.\n\n## Built-in Supported Types\n\n* `bool`, `int`, `float`, `str`, `None` encoded as it is.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      a: int\n      b: str\n      c: bool\n      d: None\n\n  Obj(a=1, b=\"b\", c=True, d=None).json()\n  # => {'a': 1, 'b': 'b', 'c': True, 'd': None}\n  ```\n\n* `Decimal` encoded to `str`.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      a: Decimal\n\n  Obj(a=Decimal(\"3.1\")).json()  # => {'a': '3.1'}\n  ```\n\n* `datetime` encoded to timestamp integer via `.timestamp()` method.\n  `timedelta` encoded to integer via `.total_seconds()` method.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      a: datetime\n      b: timedelta\n\n  Obj(a=datetime.now(), b=timedelta(minutes=1)).json()\n  # => {'a': 1660062019, 'b': 60}\n  ```\n\n* `Enum` and `IntEnum` encoded to their values via `.value` attribute.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      status: Status\n\n  Obj(status=Status.DONE).json()  # => {'status': 1}\n  ```\n\n* `Any` is encoded according to its `type`.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      a: Any\n\n  Obj(1).json()  # {'a': 1}\n  Obj(\"a\").json()  # {'a': 'a'}\n  Obj.from_json({\"a\": 1})  # Obj(a=1)\n  ```\n\n* `Optional[X]` is supported, but `Union[X, Y, ...]` is not.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      a: Optional[int] = None\n\n  Obj(a=1).json()  # => {'a': 1}\n  ```\n\n* `List[X]`, `Tuple[X]`, `Set[X]` are all encoded to `list`.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      a: List[int]\n      b: Set[int]\n      c: Tuple[int, str]\n      d: Tuple[int, ...]\n\n  Obj(a=[1], b={2, 3}, c=(4, \"5\"), d=(7, 8, 9)).json())\n  # => {'a': [1], 'b': [2, 3], 'c': [4, '5'], 'd': [7, 8, 9]}\n\n  Obj.from_json({\"a\": [1], \"b\": [2, 3], \"c\": [4, \"5\"], \"d\": [7, 8, 9]}))\n  # => Obj(a=[1], b={2, 3}, c=(4, '5'), d=(7, 8, 9))\n  ```\n\n* `Dict[str, X]` encoded to `dict`.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      a: Dict[str, int]\n  Obj(a={\"x\": 1}).json()  # => {'a': {'x': 1}}\n  Obj.from_json({\"a\": {\"x\": 1}}) # => Obj(a={'x': 1})\n  ```\n\n* Nested or recursively `JSONAble` (or `J`) dataclasses.\n\n  ```python\n  @dataclass\n  class Elem(J):\n      k: str\n\n  @dataclass\n  class Obj(J):\n      a: List[Elem]\n\n  Obj([Elem(\"v\")]).json()  # => {'a': [{'k': 'v'}]}\n  Obj.from_json({\"a\": [{\"k\": \"v\"}]})  # Obj(a=[Elem(k='v')])\n  ```\n\n* Postponed annotations (the `ForwardRef` in [PEP 563](https://www.python.org/dev/peps/pep-0563/)).\n\n  ```python\n  @dataclass\n  class Node(J):\n      name: str\n      left: Optional[\"Node\"] = None\n      right: Optional[\"Node\"] = None\n\n  root = Node(\"root\", left=Node(\"left\"), right=Node(\"right\"))\n  root.json()\n  # {'name': 'root', 'left': {'name': 'left', 'left': None, 'right': None}, 'right': {'name': 'right', 'left': None, 'right': None}}\n  ```\n\nIf these built-in default conversion behaviors do not meet your needs,\nor your type is not on the list,\nyou can use [json_options](#customization--overriding-examples) introduced below to customize it.\n\n## Customization / Overriding Examples\n\nWe can override the default conversion behaviors with `json_options`,\nwhich uses the dataclass field's metadata for field-level customization purpose,\nand the namespace is `j`.\n\nThe following pseudo code gives the pattern:\n\n```python\nfrom dataclasses import field\nfrom dataclass_jsonable import json_options\n\n@dataclass\nclass Struct(J):\n    attr: T = field(metadata={\"j\": json_options(**kwds)})\n```\n\nAn example list about `json_options`:\n\n* Specific a custom dictionary key over the default field's name:\n\n   ```python\n   @dataclass\n   class Person(J):\n       attr: str = field(metadata={\"j\": json_options(name=\"new_attr\")})\n   Person(attr=\"value\").json() # => {\"new_attr\": \"value\"}\n   ```\n\n  And more, we can use a function to specific a custom dictionary key.\n  This may be convenient to work with class-level `__default_json_options__` attribute (check it below).\n\n  ```python\n  @dataclass\n  class Obj(J):\n      simple_value: int = field(metadata={\"j\": json_options(name_converter=to_camel_case)})\n  Obj(simple_value=1).json()  # => {\"simpleValue\": 1}\n  ```\n\n  And we may specific a custom field name converter when converts dictionary to dataclass:\n\n  ```python\n  @dataclass\n  def Person(J):\n    name: str = field(\n          metadata={\n              \"j\": json_options(\n                  name_converter=lambda x: x.capitalize(),\n                  name_inverter=lambda x: \"nickname\",\n            )\n        }\n    )\n  ```\n\n  As the `Person` defined above, it will convert to dictionary like `{\"Name\": \"Jack\"}` and can be loaded from `{\"nickname\": \"Jack\"}`.\n\n* Omit a field if its value is empty:\n\n   ```python\n   @dataclass\n   class Book(J):\n       name: str = field(metadata={\"j\": json_options(omitempty=True)})\n   Book(name=\"\").json() # => {}\n   ```\n\n  Further, we can specify what is 'empty' via option `omitempty_tester`:\n\n   ```python\n   @dataclass\n   class Book(J):\n       attr: Optional[str] = field(\n           default=None,\n           metadata={\n               # By default, we test `empty` using `not x`.\n               \"j\": json_options(omitempty=True, omitempty_tester=lambda x: x is None)\n           },\n       )\n\n   Book(attr=\"\").json()  # => {'attr': ''}\n   Book(attr=None).json()  # => {}\n   ```\n\n* Always skip a field. So we can stop some \"private\" fields from exporting:\n\n   ```python\n   @dataclass\n   class Obj(J):\n       attr: str = field(metadata={\"j\": json_options(skip=True)})\n\n   Obj(attr=\"private\").json() # => {}\n   ```\n\n* Always keep a field without encoding nor decoding, this prevents the default encoding/decoding behavior:\n\n   ```python\n   @dataclass\n   class Obj(J):\n       timestamp: datetime = field(metadata={\"j\": json_options(keep=True)})\n\n   Obj(timestamp=datetime.now()).json() # =>  {'timestamp': datetime.datetime(2023, 9, 5, 14, 54, 24, 679103)}\n   ```\n\n* dataclasses's `field` allows us to pass a `default` or `default_factory` argument to\n  set a default value:\n\n  ```python\n  @dataclass\n  class Obj(J):\n      attr: List[str] = field(default_factory=list, metadata={\"j\": json_options(**kwds)})\n  ```\n\n  There's also an option `default_before_decoding` in dataclass-jsonable,\n  which specifics a default value before decoding if the key is missing in the dictionary.\n  Sometimes this way is more concise:\n\n  ```python\n  @dataclass\n  class Obj(J):\n      updated_at: datetime = field(metadata={\"j\": json_options(default_before_decoding=0)})\n\n  Obj.from_json({})  # => Obj(updated_at=datetime.datetime(1970, 1, 1, 8, 0))\n  ```\n\n  dataclass-jsonable also introduces a class-level similar option `__default_factory__`.\n  If a field has no `default` or `default_factory` declared, and has no `default_before_decoding` option used,\n  this function will generate a default value according to its type, to prevent a\n  \"missing positional arguments\" TypeError from raising.\n\n  ```python\n  from dataclass_jsonable import J, zero\n\n  @dataclass\n  class Obj(J):\n      __default_factory__ = zero\n\n      n: int\n      s: str\n      k: List[str]\n\n  Obj.from_json({})  # => Obj(n=0, s='', k=[])\n  ```\n\n* Override the default encoders and decoders.\n\n  This way, you have complete control over how to encode and decode at field level.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      elems: List[str] = field(\n          metadata={\n              \"j\": json_options(\n                  encoder=lambda x: \",\".join(x),\n                  decoder=lambda x: x.split(\",\"),\n              )\n          }\n      )\n\n  Obj(elems=[\"a\", \"b\", \"c\"]).json()  # => {'elems': 'a,b,c'}\n  Obj.from_json({\"elems\": \"a,b,c\"})  # => Obj(elems=['a', 'b', 'c'])\n  ```\n\n  The following code snippet about `datetime` is a very common example,\n  you might want ISO format datetime conversion over timestamp integers.\n\n  ```python\n  @dataclass\n  class Record(J):\n      created_at: datetime = field(\n          default_factory=datetime.now,\n          metadata={\n              \"j\": json_options(\n                  encoder=datetime.isoformat,\n                  decoder=datetime.fromisoformat,\n              )\n          },\n      )\n\n  Record().json()  # => {'created_at': '2022-08-09T23:23:02.543007'}\n  ```\n\n  dataclass-jsonable gives `encoder` and `decoder` better alias names since 0.1.1:\n  `to_json` and `from_json`.\n\n  ```python\n  @dataclass\n  class Obj(J):\n      elems: List[str] = field(\n          metadata={\n              \"j\": json_options(\n                  to_json=lambda x: \",\".join(x),  # Alias for encoder\n                  from_json=lambda x: x.split(\",\"),  # Alias for decoder\n              )\n          }\n      )\n\n  Obj(elems=[\"a\", \"b\", \"c\"]).json()  # => {'elems': 'a,b,c'}\n  Obj.from_json({\"elems\": \"a,b,c\"})  # => Obj(elems=['a', 'b', 'c'])\n  ```\n\n* For some very narrow scenarios, we may need to execute a hook function before decoding,\n  for example, the data to be decoded is a serialized json string,\n  and but we still want to use the built-in decoder functions instead of making a new decoder.\n\n  ```python\n  import json\n\n  @dataclass\n  class Obj(J):\n      data: Dict[str, Any] = field(metadata={\"j\": json_options(before_decoder=json.loads)})\n\n  Obj.from_json({\"data\": '{\"k\": \"v\"}'})\n  # => Obj(data={'k': 'v'})\n  ```\n\n* Customize default behaviors at the class level.\n\n  If an option is not explicitly set at the field level,\n  the `__default_json_options__` provided at the class level will be attempted.\n\n  ````python\n  @dataclass\n  class Obj(J):\n      __default_json_options__ = json_options(omitempty=True)\n\n      a: Optional[int] = None\n      b: Optional[str] = None\n\n  Obj(b=\"b\").json() # => {'b': 'b'}\n  ````\n\n  ```python\n  @dataclass\n  class Obj(J):\n      __default_json_options__ = json_options(name_converter=to_camel_case)\n\n      status_code: int\n      simple_value: str\n\n  Obj2(status_code=1, simple_value=\"simple\").json()\n  # => {\"statusCode\": 1, \"simpleValue\": \"simple\"}\n  ```\n\n## Debuging\n\nIt provides a method `obj._get_origin_json()`,\nit returns the original json dictionary which constructs instance `obj` via `from_json()`.\n\n```python\nd = {\"a\": 1}\nobj = Obj.from_json(d)\nobj._get_origin_json()\n# => {\"a\": 1}\n```\n\n## License\n\nBSD.\n",
    "bugtrack_url": null,
    "license": "BSD",
    "summary": "Simple and flexible conversions between dataclasses and jsonable dictionaries.",
    "version": "0.1.3",
    "project_urls": {
        "Homepage": "https://github.com/hit9/dataclass-jsonable"
    },
    "split_keywords": [
        "dataclasses",
        "json",
        "jsonable"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "9113ebace88a4f9cdead6e8489641ac188242a55da0e8673d877e41ec92809c3",
                "md5": "37ea794d2e741d718ae9782cb6750b99",
                "sha256": "6bcfa8f31bb06b847cfe007ddf0c976d220c36bc28fe47660ee71a673b90347c"
            },
            "downloads": -1,
            "filename": "dataclass-jsonable-0.1.3.tar.gz",
            "has_sig": false,
            "md5_digest": "37ea794d2e741d718ae9782cb6750b99",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7",
            "size": 11639,
            "upload_time": "2023-09-06T06:11:08",
            "upload_time_iso_8601": "2023-09-06T06:11:08.234972Z",
            "url": "https://files.pythonhosted.org/packages/91/13/ebace88a4f9cdead6e8489641ac188242a55da0e8673d877e41ec92809c3/dataclass-jsonable-0.1.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-09-06 06:11:08",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "hit9",
    "github_project": "dataclass-jsonable",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "dataclass-jsonable"
}
        
Elapsed time: 0.10967s