Metadata-Version: 2.3
Name: jsonstar
Version: 1.1.0
Summary: Extensible JSON module to serialize all objects.
License: MIT
Author: Henrique Bastos
Author-email: henrique@bastos.net
Requires-Python: >=3.9
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Project-URL: Documentation, https://github.com/henriquebastos/python-jsonstar
Project-URL: Homepage, https://github.com/henriquebastos/python-jsonstar
Project-URL: Issues, https://github.com/henriquebastos/python-jsonstar/issues
Project-URL: Repository, https://github.com/henriquebastos/python-jsonstar
Description-Content-Type: text/markdown

# JSON* is an extensible json module to serialize all objects!

`jsonstar` extends Python's standard JSON encoder and decoder to easily handle your custom types.

This means you won't have to transform your custom types into dictionaries with primitive types before encoding them to
JSON. And you won't have to parse back the encoded strings into your custom types after decoding them from JSON.

## How to install it?

```bash
pip install jsonstar
````

## How to start using it?

The `jsonstar` module provides the same API as the standard `json` module, so you can use it as a drop in replacement.

Simply change your import from `import json` to `import jsonstar as json` and you're good to go.

## Why use it?

Consider you have a pydantic `Employee` class that you want to serialize to JSON.

```python
from decimal import Decimal
from datetime import date
from pydantic import BaseModel

class Employee(BaseModel):
    name: str
    salary: Decimal
    birthday: date
    roles: set

employee = Employee(
    name="John Doe",
    salary=Decimal("1000.00"),
    birthday=date(1990, 1, 1),
    roles={"A", "B", "C"},
)
```

The standard `json` module can't serialize the `employee` instance, requiring you to call its `dict` method.
This will not sufice, because the standard `json` module don't know how to encode `Decimal`, `date` and `set`.
Your solution would include some trasnfomation of the `employee` instance and its attributes before encoding it to JSON.

That is where `jsonstar` shines by providing default encoder for common types like `pydantic.BaseModel`,
`decimal.Decimal`, `datetime.date` and `set`. And allowing you to easily add your own encoders.

```python
from jsonstar as json
print(json.dumps(employee))
# {"name": "John Doe", "salary": "1000.00", "birthday": "1990-01-01", "roles": ["A", "B", "C"]}
```

## What default encoders are provided?

By default, `jsonstar` provides encoders for the following types:

- `attrs` classes
- `dataclasses.dataclass` classes
- `datetime.date`
- `datetime.datetime`
- `datetime.time`
- `datetime.timedelta`
- `decimal.Decimal`
- `frozenset`
- `pydantic.BaseModel`
- `set`
- `uuid.UUID`

### Configurable default encoders

For some complex types like Django Models, an opinionated default encoder would not work for everyone.

This is why instead of providing a default encoder for Django Models, `jsonstar` provides you a configurable
encoder class to allow you to define the desired encoding behavior.

```python
import jsonstar as json
from jsonstar import DjangoModelEncoder

json.register_default_encoder(Model, DjangoModelEncoder(exclude=[DjangoModelEncoder.RELATIONSHIPS]))
```

The above code will register a default encoder for the `Model` class that will return all fields, excluding
relationships.

### Can `jsonstar` add more default encoders?

Yes. If you think that a default encoder for a common type is missing, please open an issue or a pull request.
See the *How to contribute* section for more details.

## How do I add my own encoder?

First you need to decide if you want your encoder to be available everywhere on your project or just for a specific
code block.

- *Default encoders* are globally available and will be used anywhere in your project.
- *Instance encoders* are available only for the `JSONEncoderStar` instance that you register them.

Also you have two types of encoders to choose from:

- *Typed encoders* are used to encode a specific type identified by `isinstance`.
- *Functional encoders* are used to encode an object based on arbitraty logic.

### How to add a default encoder?

To add a default encoder use the `register_default_encoder` function on the `jsonstar` module.

```python
import jsonstar as json
from decimal import Decimal


def two_decimals_encoder(obj):
    """Encodes a decimal with only two decimal places."""
    return str(obj.quantize(Decimal("1.00")))


json.register_default_encoder(Decimal, two_decimals_encoder)
```

### How to add an instance encoder?

An instance encoder can be added in three ways:

1. Using the `register` method on the `JSONEncoderStar` instance.
2. Passing the encoder to the `JSONEncoderStar` initialization.
3. Passing the encoder to the `dumps` function.

```python
import jsonstar as json
from decimal import Decimal


def two_decimals_encoder(obj):
    """Encodes a decimal with only two decimal places."""
    return str(obj.quantize(Decimal("1.00")))


# 1. Using the `register` method on the `JSONEncoderStar` instance.
encoder1 = json.JSONEncoderStar()
encoder1.register(two_decimals_encoder, Decimal)

# 2. Passing the encoder to the `JSONEncoderStar` initialization.
encoder2 = json.JSONEncoderStar(typed_encoders={Decimal: two_decimals_encoder})

# 3. Passing the encoder to the `dumps` function.
print(json.dumps(data, cls={Decimal: two_decimas_encoder}))
```

### How to add a typed encoder?

*Typed encoders* are specific to a type and it's inherited types.

When registering a typed encoder, you simply pass the encoder and the type to the chosen registration method.

When you add a typed encoder, `jsonstar` will check if any base class already has a registered encoder make sure the
more generic encoder is used last, respecting Python's Method Resolution Order (MRO).


### How to add a functional encoder?

*Functional encoders* are used to encode an object based on arbitraty logic and not specific to a type.

To register a functional encoder, you simply pass the encoder to the chosen registration method omiting the type.

All functional encoders are called only for objects that do not have a registered typed encoder.

## Contributing
Pull requests are welcome and must have associated tests.

For major changes, please open an issue first to discuss what you would like to change.


## License
[MIT](https://choosealicense.com/licenses/mit/)

## Author
Henrique Bastos <henrique@bastos.net>

## Project links
- [Homepage](https://github.com/henriquebastos/python-jsonstar)
- [Repository](https://github.com/henriquebastos/python-jsonstar)
- [Documentation](https://github.com/henriquebastos/python-jsonstar)

