jsonbourne¶
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
andattrs
- FastAPI:
- JSONBOURNEResponse ~ auto use the best
- No hard dependencies ~ works with python-stdlib-json as well as
orjson
andpython-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:
In [1]:
Copied!
# Importing JSON:
# or
import JSON
# Importing jsonbourne:
from jsonbourne import JSON
# Importing JSON:
# or
import JSON
# Importing jsonbourne:
from jsonbourne import JSON
JSON basics:
In [2]:
Copied!
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
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
Out[2]:
'{"a":1,"b":2,"c":3}'
JSON option kwargs ~ pretty
& sort_keys
¶
pretty:
In [3]:
Copied!
string_dumps = JSON.stringify(
{"b": 2, "a": 1, "c": 3}, pretty=True
) # '{"a": 1, "b": 2, "c": 3}'
print(string_dumps)
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:
In [4]:
Copied!
string_dumps = JSON.stringify(
{"b": 2, "a": 1, "c": 3}, pretty=True, sort_keys=True
) # '{"a": 1, "b": 2, "c": 3}'
print(string_dumps)
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
withjsonbourne.JSON
- Recursive jsonification
- Allows for kwarging (
**json_obj
) - Works with
pydantic
andattrs
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¶
In [5]:
Copied!
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)
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)
Out[5]:
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¶
In [6]:
Copied!
JSON(data).sub.b
JSON(data).sub.b
Out[6]:
3
In [7]:
Copied!
stringified_data = JSON(data).stringify(pretty=True)
print(stringified_data)
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 }
In [8]:
Copied!
parsed_data = JSON(stringified_data)
parsed_data
parsed_data = JSON(stringified_data)
parsed_data
Out[8]:
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 })
In [9]:
Copied!
list(parsed_data.keys())
list(parsed_data.keys())
Out[9]:
['key', 'list', 'dt', 'sub', 'timedelta']
In [10]:
Copied!
list(parsed_data.items())
list(parsed_data.items())
Out[10]:
[('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)]
In [11]:
Copied!
list(parsed_data.dot_keys())
list(parsed_data.dot_keys())
Out[11]:
[('key',), ('list',), ('dt',), ('sub', 'b'), ('sub', 'key'), ('sub', 'a'), ('timedelta',)]
In [12]:
Copied!
list(parsed_data.dot_items())
list(parsed_data.dot_items())
Out[12]:
[(('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)]
In [13]:
Copied!
parsed_data[("sub", "key")]
parsed_data[("sub", "key")]
Out[13]:
'val'
In [14]:
Copied!
parsed_data.dot_lookup("sub.key")
parsed_data.dot_lookup("sub.key")
Out[14]:
'val'
In [15]:
Copied!
{**parsed_data}
{**parsed_data}
Out[15]:
{'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}
In [16]:
Copied!
# fully eject
parsed_data.eject()
# fully eject
parsed_data.eject()
Out[16]:
{'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.
In [17]:
Copied!
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]
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]})>
In [18]:
Copied!
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
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
Out[18]:
JsonObjModel(**{ 'a': 1, 'b': 2, 'c': 'herm', 'd': JsonObj(**{'nested': 'nestedval'}), 'e': {'herm': 2} })
In [19]:
Copied!
# respects properties (which I don't think pydantic does(currently))
obj.a_property
# respects properties (which I don't think pydantic does(currently))
obj.a_property
Out[19]:
'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:
rapidjson
orjson
Custom lib preferences¶
In [20]:
Copied!
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)
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.RAPIDJSON'> {"a":1,"b":2,"c":3}