Skip to main content

EZPZ JSON

Project description

drawing

jsonbourne

Wheel Version py_versions Code style: black

Install: pip install jsonbourne

  • Python json lib/pkg that makes json feel like the JSON module in javascript/typescript:
    • from jsonbourne import JSON; JSON.parse(JSON.stringify({"key": "value"}))
    • Automatically uses best json-lib-backend available (orjson/python-rapidjson) ~ can be configured
  • Hybrid dict/class object (jsonbourne.JsonObj):
    • Dot-notation getting/setting (featuring protected attributes!)
    • All your favorite python dictionary methods (items, keys, update, values) and more!
    • Works with pydantic and attrs
  • FastAPI:
    • JSONBOURNEResponse ~ auto use the best
  • No hard dependencies ~ works with python-stdlib-json as well as orjson and python-rapidjson
  • jsonbourne.JsonObj uses list/dict comprehensions (some are recursive) everywhere because 'why not?' and it is a bit faster

Usage:

JSON ~ from jsonbourne import JSON

Importing:

# Importing JSON:
from jsonbourne import JSON

# or
import JSON

# Importing jsonbourne:
import jsonbourne
import david_webb  # jsonbourne's `True` identity

JSON basics:

import JSON  # Module included with jsonbourne

string_stringify = JSON.stringify(
    {"a": 1, "b": 2, "c": 3}
)  # '{"a": 1, "b": 2, "c": 3}'
string_dumps = JSON.dumps({"a": 1, "b": 2, "c": 3})  # '{"a": 1, "b": 2, "c": 3}'
string_dumps
'{"a":1,"b":2,"c":3}'

JSON option kwargs ~ pretty & sort_keys

pretty:

string_dumps = JSON.stringify(
    {"b": 2, "a": 1, "c": 3}, pretty=True
)  # '{"a": 1, "b": 2, "c": 3}'
print(string_dumps)
{
  "b": 2,
  "a": 1,
  "c": 3
}

sort_keys:

string_dumps = JSON.stringify(
    {"b": 2, "a": 1, "c": 3}, pretty=True, sort_keys=True
)  # '{"a": 1, "b": 2, "c": 3}'
print(string_dumps)
{
  "a": 1,
  "b": 2,
  "c": 3
}

JsonObj & JSON

  • Python dictionary/object with dot access
  • Protections against setting class/obj attributes
  • Is as javascript-y as possible (keys have to be strings -- ints/floats will be converted to strings)
  • Create a jsonbourne.JsonObj with jsonbourne.JSON
  • Recursive jsonification
  • Allows for kwarging (**json_obj)
  • Works with pydantic and attrs

Make an empty JsonObj

The following 3 examples all produce the same thing

from jsonbourne import JSON
j = JSON()  # j => JsonObj(**{})
# OR
import JSON
j = JSON()  # j => JsonObj(**{})
# OR
from jsonbourne import JsonObj
j = JsonObj()  # j => JsonObj(**{})

From a dictionary o data

import datetime

data = {
    "key": "value",
    "list": [1, 2, 3, 4, 5],
    "dt": datetime.datetime(1970, 1, 1, 0, 0, 0, 1),
    "sub": {
        "b": 3,
        "key": "val",
        "a": 1,
    },
    "timedelta": datetime.timedelta(days=2),
}

JSON(data)
JsonObj(**{
    'dt': datetime.datetime(1970, 1, 1, 0, 0, 0, 1),
    'key': 'value',
    'list': [1, 2, 3, 4, 5],
    'sub': {'a': 1, 'b': 3, 'key': 'val'},
    'timedelta': datetime.timedelta(days=2)
})

Dot access

JSON(data).sub.b
3
stringified_data = JSON(data).stringify(pretty=True)
print(stringified_data)
{
  "key": "value",
  "list": [
    1,
    2,
    3,
    4,
    5
  ],
  "dt": "1970-01-01T00:00:00.000001",
  "sub": {
    "b": 3,
    "key": "val",
    "a": 1
  },
  "timedelta": 172800.0
}
parsed_data = JSON(stringified_data)
parsed_data
JsonObj(**{
    'dt': '1970-01-01T00:00:00.000001',
    'key': 'value',
    'list': [1, 2, 3, 4, 5],
    'sub': {'a': 1, 'b': 3, 'key': 'val'},
    'timedelta': 172800.0
})
list(parsed_data.keys())
['key', 'list', 'dt', 'sub', 'timedelta']
list(parsed_data.items())
[('key', 'value'),
 ('list', [1, 2, 3, 4, 5]),
 ('dt', '1970-01-01T00:00:00.000001'),
 ('sub', JsonObj(**{'b': 3, 'key': 'val', 'a': 1})),
 ('timedelta', 172800.0)]
list(parsed_data.dot_keys())
[('key',),
 ('list',),
 ('dt',),
 ('sub', 'b'),
 ('sub', 'key'),
 ('sub', 'a'),
 ('timedelta',)]
list(parsed_data.dot_items())
[(('key',), 'value'),
 (('list',), [1, 2, 3, 4, 5]),
 (('dt',), '1970-01-01T00:00:00.000001'),
 (('sub', 'b'), 3),
 (('sub', 'key'), 'val'),
 (('sub', 'a'), 1),
 (('timedelta',), 172800.0)]
parsed_data[("sub", "key")]
'val'
parsed_data.dot_lookup("sub.key")
'val'
{**parsed_data}
{'key': 'value',
 'list': [1, 2, 3, 4, 5],
 'dt': '1970-01-01T00:00:00.000001',
 'sub': JsonObj(**{'b': 3, 'key': 'val', 'a': 1}),
 'timedelta': 172800.0}
# fully eject
parsed_data.eject()
{'key': 'value',
 'list': [1, 2, 3, 4, 5],
 'dt': '1970-01-01T00:00:00.000001',
 'sub': {'b': 3, 'key': 'val', 'a': 1},
 'timedelta': 172800.0}

Protected keys

jsonbourne.JsonObj protects against setting attributes like 'items' through dot-notation.

from jsonbourne import JSON

j = JSON()
j.key = "value"
try:  # CANNOT set 'items' using dot-access
    j.items = [1, 2, 3, 4]
except ValueError:
    pass
# CAN set 'items' through key/item access
j["items"] = [1, 2, 3, 4]
print(j.__dict__)
print(j)
j_items = j.items
print("items", j_items)
# Getting 'items' through dot-access returns the `items()` method
assert j.items != [1, 2, 3, 4]
# Getting 'items' with key-access returns the stored value
assert j["items"] == [1, 2, 3, 4]
{'_data': {'key': 'value', 'items': [1, 2, 3, 4]}}
JsonObj(**{
    'items': [1, 2, 3, 4], 'key': 'value'
})
items <bound method JsonObj.items of JsonObj(**{'key': 'value', 'items': [1, 2, 3, 4]})>

pydantic & jsonbourne

  • from jsonbourne.pydantic import JsonBaseModel
  • Allows for aliases when getting/setting attribute(s)
  • Supports __post_init__ (like dataclasses)

Basic usage:

from jsonbourne import JsonObj
from jsonbourne.pydantic import JsonBaseModel


class JsonSubObj(JsonBaseModel):
    herm: int

    def to_dict(self):
        return self.dict()

    def to_json(self, *args, **kwargs):
        return self.json()

    @classmethod
    def from_json(cls, json_string: str):
        return JsonSubObj(json.loads(json_string))


class JsonObjModel(JsonBaseModel):
    a: int
    b: int
    c: str
    d: JsonObj
    e: JsonSubObj

    #
    @property
    def a_property(self) -> str:
        return "prop_value"

    def to_json(self, *args, **kwargs):
        return self.json()

    @classmethod
    def from_json(cls, json_string: str):
        return cls(**json.loads(json_string))


obj = JsonObjModel(
    **{"a": 1, "b": 2, "c": "herm", "d": {"nested": "nestedval"}, "e": {"herm": 2}}
)
obj
JsonObjModel(**{
     'a': 1,
     'b': 2,
     'c': 'herm',
     'd': JsonObj(**{'nested': 'nestedval'}),
     'e': {'herm': 2}
})
# respects properties (which I don't think pydantic does(currently))
obj.a_property
'prop_value'

JSON backend/lib

jsonbourne finds the best json-lib (python-rapidjson/orjson) it can! On import jsonbourne gets to work spying on your python env. jsonbourne, the most highly qualified json-CIA-agent, will import the best python-json library it can find; if jsonbourne's cover is blown (meaning: the only json library found is the python stdlib json), jsonbourne will fallback to the python stdlib json.

jsonbourne will look for the following json-packages in the following order:

  1. rapidjson
  2. orjson

Custom lib preferences

from jsonbourne import import_json

json = import_json(("rapidjson", "orjson"))  # prefer rapidjson over orjson
string = json.dumps({"a": 1, "b": 2, "c": 3})
print(json)
print(string)
<class 'jsonbourne.jsonlib.RAPIDJSON'>
{"a":1,"b":2,"c":3}

Installing better JSON lib:

orjson

  • pip install orjson [pip]

rapidjson/python-rapidjson

  • pip install python-rapidjson [pip]
  • conda install -c anaconda python-rapidjson [conda anaconda/defaults]
  • conda install -c conda-forge python-rapidjson [conda-forge]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jsonbourne-0.29.0.tar.gz (25.8 kB view details)

Uploaded Source

Built Distribution

jsonbourne-0.29.0-py3-none-any.whl (27.5 kB view details)

Uploaded Python 3

File details

Details for the file jsonbourne-0.29.0.tar.gz.

File metadata

  • Download URL: jsonbourne-0.29.0.tar.gz
  • Upload date:
  • Size: 25.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.10.12 Linux/5.15.146.1-microsoft-standard-WSL2

File hashes

Hashes for jsonbourne-0.29.0.tar.gz
Algorithm Hash digest
SHA256 8d2efb1d44eec3796484243592a76f6e4c15208311da39de1fc4937faba5ea79
MD5 47c447918e1d8363e06318a7706ce669
BLAKE2b-256 8ed38c9b831e49658afc055a89283a1ea714b7a55d416426c4133655a1c05ab5

See more details on using hashes here.

File details

Details for the file jsonbourne-0.29.0-py3-none-any.whl.

File metadata

  • Download URL: jsonbourne-0.29.0-py3-none-any.whl
  • Upload date:
  • Size: 27.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.7.1 CPython/3.10.12 Linux/5.15.146.1-microsoft-standard-WSL2

File hashes

Hashes for jsonbourne-0.29.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3571563f838a214dcbe680d54fc06c9fb841f82a648b615576e13d8855ae2dd0
MD5 8a6094fa861ab7f34cee89e915d436e7
BLAKE2b-256 851f5f7b865b84e902c7cd081e6a8d68723d7108df019c57af84fe4c02f73790

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page