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’s SimpleNamespace.

  • 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() or loads_simplified() to get this behavior; plain dict/list/tuple will not auto-return Undefined.

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

Next: Installation