Introduction
Welcome to jsify – a Python library with a blazing fast C extension that brings JavaScript-like dot access, lazy dynamic wrapping, and safe deep data handling to your nested structures. jsify is designed for complex, unpredictable, or deeply nested JSON and Python data, delivering high performance for common dynamic access patterns.
Note
This library uses a C extension for ultra-fast access to your data structures.
Contents
Concepts
Key Object Types: SimplifiedObject vs jsify objects
jsify provides two main dynamic wrappers:
SimplifiedObject
: For simple dot-access with missing-attribute safety, modeled after Python’sSimpleNamespace
.jsify
wrappers (Object
,Dict
,List
, etc): For deep, JavaScript-style dot/item access and automatic lazy wrapping.
You can use either approach depending on your requirements. See below for precise behaviors.
SimplifiedObject: Simple & Safe Dot Access
A SimplifiedObject
is like an improved SimpleNamespace
:
Dot access for every key—missing attributes return
Undefined
instead of raising errors.Only the top-level dict is converted by default; nested dicts remain dicts unless you recursively wrap or use
loads_simplified
with an object hook to convert all levels.
Example:
from jsify.simplify import loads_simplified, Undefined
data = '{"user": {"profile": null}}'
obj = loads_simplified(data)
# Works: user exists (SimplifiedObject), profile is None (no error).
if obj.user.profile is None:
print("No profile")
# If 'profile' is a dict and you want further dot access,
# use loads_simplified on nested dicts as needed.
Missing attributes on a SimplifiedObject
always return Undefined
—not errors. However, deep safe chaining only works for dicts converted to SimplifiedObject
.
Another Example:
data = '{"a": 1, "b": 2}'
obj = loads_simplified(data)
print(obj.a) # 1
print(obj.z) # <Undefined>, no error
# If obj.b is a dict, you must wrap it for dot access on its keys.
jsify Objects: Advanced Dynamic Wrapping
jsify
turns dicts, lists, or tuples (even deeply nested) into JavaScript-style objects:
Blazing fast: Built on a C extension for top performance.
Lazy wrapping: Only wraps as you access, not eagerly.
Reflects changes in the original structure; wrappers are views, not copies.
Supports both dot and item access, just like in JavaScript.
Example:
from jsify import jsify
data = {"settings": {"theme": "dark"}, "items": [1, 2, 3]}
obj = jsify(data)
# Dot access
print(obj.settings.theme) # "dark"
# List access
print(obj.items[1]) # 2
# Changes in the original dict are reflected in the wrapper
data["settings"]["theme"] = "light"
print(obj.settings.theme) # "light"
Nested mutation example:
orig = {"foo": {"bar": 1}}
o = jsify(orig)
print(o.foo.bar) # 1
orig["foo"]["bar"] = 2
print(o.foo.bar) # 2
Undefined: Error-Free Access for Missing Keys
Undefined
is a special singleton returned for any missing key or attribute on jsify wrappers or SimplifiedObject
. It behaves like JavaScript’s undefined
:
All missing keys/attributes return
Undefined
when accessed on a jsify or SimplifiedObject wrapper.Undefined
is safe to chain: any attribute/item access on it yields itself, and it is falsy in boolean checks.Note: You must use
jsify()
orloads_simplified()
to get this behavior; plain dict/list/tuple will not auto-returnUndefined
.
Example:
wrapped = jsify({"user": {}})
print(wrapped.user.profile.email) # <Undefined>, never raises error
if wrapped.user.profile.email is Undefined:
print("Email not available")
Boolean check:
if not wrapped.user.notexisting.anything:
print("No value!") # This line executes because Undefined is falsy
Advantages
Why SimplifiedObject?
Never raises exceptions for missing attributes—returns
Undefined
.Cleaner code: no need for multiple checks or try/except blocks.
Instantly converts JSON/dict to dot-access objects for convenient use.
Can be used with the
object_hook
to recursively wrap dicts from JSON.
See example above at SimplifiedObject: Simple & Safe Dot Access.
Why jsify?
Blazing fast C extension: Efficient even with very large or deeply nested data.
Lazy and reference-based: You see changes live, as objects are mutated.
Handles all nestings: Works for dicts, lists, tuples, and deep chains.
Safe chaining: Missing paths never error—just return
Undefined
.JSON (de)serialization: Can automatically omit or include
Undefined
values.
See example above at jsify Objects: Advanced Dynamic Wrapping.
Example: Serialization
from jsify.json import jsified_dumps, Undefined
obj = jsify({"a": 1, "b": None, "c": Undefined})
print(jsified_dumps(obj)) # {"a": 1, "b": null}
print(jsified_dumps(obj, omit_undefined=False)) # {"a": 1, "b": null, "c": null}
No Key Conflicts
Your data’s keys always take priority (e.g.,
obj.items
gives the value of"items"
in your dict, not the method).Use helper functions for classic methods:
jsified_items(obj)
,jsified_update(obj, data)
, etc.Or unwrap with
unjsify(obj)
to get back the original dict and standard methods.
Example:
from jsify import jsify, jsified_items, unjsify
data = {"items": "DATA"}
obj = jsify(data)
print(obj.items) # "DATA" (your dict value)
print(list(jsified_items(obj))) # dict items, e.g. [("items", "DATA")]
# Or get classic dict methods:
orig = unjsify(obj)
print(list(orig.items())) # [('items', 'DATA')]
Example: Key masking built-ins
data = {"update": "NOT_A_METHOD"}
obj = jsify(data)
print(obj.update) # "NOT_A_METHOD"
# Still call classic update via helper:
from jsify import jsified_update
jsified_update(obj, {"x": 5})
print(obj.x) # 5