pycjson 1.0.0
A rich-feature JSON parser library for Python.
License: MIT License

# pycjson
A rich-feature and fast JSON parser library for Python.

**What is the different from others JSON parsers:**
- Supports python classes - converts JSON to Python types and vice versa (**BIG DIFFERENT**)
- Fast and powerful - using [ujson](https://github.com/ultrajson/ultrajson) (fastest JSON parser library)

**requirements:**
- [ujson](https://github.com/ultrajson/ultrajson)

**content**:
- [Installing](#installing)
- [How to use it](#usage)
- [How to use `JTag`](#jtag)
- [How to use `JNested`](#jnested)
- [How to use `JList`](#jlist)
- [Strict loads - loads lists](#strict-loads)
- [License](#license)

## Installing
You can install it by **PIP**:
```bash
pip3 install pycjson
```

if you want to check installation, can try it:
```python
>>> import pycjson
>>> pycjson.__version__
'v...'
```

## Usage
We have two function for loading and dumping a JSON, `loads` and `dumps`, like all JSON parsers in Python; but have a big different that **you can specify which type you want to loads in** and **you can convert a python class to JSON**. see examples:

**for `loads`**:
```python
import pycjson

data = '{"id":1, "name":"awolverp"}'
print(pycjson.loads(data, dict))
# {"id":1, "name":"awolverp"}
```

And you can create a type for it:
```python
class User:
    id: int
    name: str

obj = pycjson.loads(data, User)
print(obj.id, obj.name)
# 1, "awolverp"
```

> Note: the `loads` function is type sensitive.

**for `dumps`**:
```python
import pycjson

print(pycjson.dumps({"id": 1, "name": "awolverp"}))
# '{"id":1,"name":"awolverp"}'
```

And you can use a type:
```python
class User:
    id: int
    name: str

    def __init__(self, id, name):
        self.id = id
        self.name = name

print(pycjson.dumps(User(1, "awolverp")))
# '{"id":1,"name":"awolverp"}'
```

> Note: the `dumps` function does not pay attention to types like `loads`.

> Note: you should specify `annontations` (we called `fields` in this library) in your types.

### JTag
**pycjson** allows you to specify some information about your fields to make for you work easy.
You can use `JTag`, `JNested`, and `JList` for it.

With `JTag` class you can:
- set name for a field in JSON data (`name` parameter).
- set default value (`_default` parameter).
- tell **pycjson** to ignore it (`ignore` parameter).
- or you can tell **pycjson** to doesn't dump it when it isn't specify (`omitempty` parameter).

See example:
```python
class User:
    id: pycjson.JTag[int] # or pycjson.JTag(int); not different!
    name: pycjson.JTag(str, _default="nothing")
    unique_name: pycjson.JTag(str, name="username", omitempty=True)
    uuid: pycjson.JTag(ignore=True)

    def __init__(self, id, name, unique_name, uuid):
        self.id = id
        self.name = name
        self.unique_name = unique_name
        self.uuid = uuid

obj = pycjson.loads('{"id":1, "username":"awolverp", "uuid":"UUID"}', User)
print(obj.id, obj.name, obj.unique_name) # uuid is undefined because pycjson ignored it and not set any value for it
# 1 "nothing" "awolverp"

data = pycjson.dumps(User(1, "Ali", "awolverp", "UUID"))
print(data)
# '{"id":1,"name":"Ali","username":"awolverp"}'

data = pycjson.dumps(User(1, "Ali", pycjson.NotSpecified, "UUID"))
print(data)
# '{"id":1,"name":"Ali"}'
```

**What is pycjson.NotSpecified?** When for a field `omitempty` is True and you don't want to dump it into JSON, set `pycjson.NotSpecified` as value for it, like this example.

### JNested
`JNested` class is like `JTag`, but it used for *nested classes* and cannot be ignored.

See example:
```python
class Image:
    url: str

    def __init__(self, url):
        self.url = url

class User:
    id: int
    image: pycjson.JNested(Image)

    def __init__(self, id, image):
        self.id = id
        self.image = image

obj = pycjson.loads('{"id": 1, "image": {"url": "URL"}}', User)
print(obj.id, obj.image.url)
# 1 "URL"

obj = pycjson.loads('{"id": 2, "image": null}', User)
print(obj.id, obj.image)
# 2 None

obj = pycjson.loads('{"id": 3}', User)
print(obj.id, obj.image)
# 3 None

data = pycjson.dumps(User(1, Image("URL")))
print(data)
# '{"id":1,"image": {"url": "URL"}}'
```

### JList
`JList` class is like `JNested`, but it works like lists and you can use it in [strict loads'](#strict-loads).

See example:
```python
class Image:
    url: str

    def __init__(self, url):
        self.url = url

    def __repr__(self):
        return f"Image({self.url})"

class DataSet:
    ids: pycjson.JList(int, name="id")
    images: pycjson.JList(Image, name="image")

    def __init__(self, ids, images):
        self.ids = ids
        self.images = images

obj = pycjson.loads('{"id": [1, 2, 3, 4], "image": [{"url": "URL"}]}', DataSet)
print(obj.ids, obj.images)
# [1, 2, 3, 4] [Image(URL)]

obj = pycjson.loads('{"id": 2, "image": {"url": "URL"}}', DataSet)
# TypeError: cannot loads int into value of type (list of) ints
```

#### Strict loads
the `loads` function has `strict` parameter. default is `False`.

if the JSON data type is list, it returns list of `_type`. see the example to understand:

```python
pycjson.loads('[1, 2, 3]', int)
# [1, 2, 3]
pycjson.loads('[1, 2, 3]', pycjson.JList[int], strict=True)
# [1, 2, 3]
pycjson.loads('[1, 2, 3]', int, strict=True)
# TypeError: cannot loads list into value of type int (strict enabled)

pycjson.loads('[1, "a", 3.2]', list, strict=True)
# [1, "a", 3.2]
pycjson.loads('[1, "a", 3.2]', pycjson.JList[int], strict=True)
# TypeError: cannot loads str into value of type int
```

## License
License: **MIT License**