Skip to content

Latest commit

 

History

History
209 lines (160 loc) · 5.51 KB

File metadata and controls

209 lines (160 loc) · 5.51 KB

schema2object — Python Reference

JSON Schema Draft-07 as a Python object. Structure maps to attributes, logic maps to methods.

pip install schema2object

Core Idea

A JS object has state and behavior together. JSON serialization stripped the behavior. schema2object restores it: the schema defines the class, the data is the instance.

from schema2object import ObjectTree

schema = {
    "type": "object",
    "properties": {
        "email": {"type": "string", "format": "email"},
        "age":   {"type": "integer", "minimum": 0},
        "roles": {"type": "array", "items": {"enum": ["admin", "user", "guest"]}}
    },
    "required": ["email", "roles"]
}

data = {"email": "alice@example.com", "age": 30, "roles": ["admin"]}
user = ObjectTree(data, schema=schema)

# Dot-access (structure → attributes)
user.email        # "alice@example.com"
user.age          # 30

Draft-07 Logic → Methods

Each Draft-07 logical operator maps to a method on ObjectTree:

Draft-07 keyword Method Semantics
oneOf one_of() XOR — exactly one branch matches
anyOf any_of() OR — all matching branches
allOf all_of() AND — merge all sub-schemas
not not_of() NOT — data must not match
if/then/else if_then() CASE WHEN — conditional branch
properties project() SELECT — keep only schema-defined fields
contains contains() EXISTS — array element check

Usage Patterns

oneOf — dispatch by type

event_schema = {
    "oneOf": [
        {"properties": {"type": {"const": "text"},   "content": {"type": "string"}}},
        {"properties": {"type": {"const": "image"},  "url": {"type": "string"}}},
        {"properties": {"type": {"const": "stream"}, "delta": {"type": "string"}}}
    ]
}

event = ObjectTree({"type": "text", "content": "hello"}, schema=event_schema)
branch = event.one_of()   # ObjectTree bound to matching sub-schema
branch.content            # "hello"

if_then — conditional logic

user_schema = {
    "if":   {"properties": {"role": {"const": "admin"}}},
    "then": {"properties": {"level": {"minimum": 5}}},
    "else": {"properties": {"level": {"maximum": 4}}}
}

user = ObjectTree({"role": "admin", "level": 10}, schema=user_schema)
branch = user.if_then()   # bound to "then" branch
branch.schema.level       # {"minimum": 5}

project — strip unknown fields

schema = {
    "properties": {
        "name": {"type": "string"},
        "age":  {"type": "integer"}
    }
}

raw = ObjectTree({"name": "Alice", "age": 30, "extra": "noise"}, schema=schema)
clean = raw.project()     # {"name": "Alice", "age": 30}

x-methods — call method specs from schema

When your schema defines x-methods, use the method name to look up and evaluate:

schema = {
    "properties": {"age": {"type": "integer", "minimum": 0}},
    "x-methods": {
        "is_adult": {
            "properties": {"age": {"minimum": 18}}
        }
    }
}

user = ObjectTree({"age": 20}, schema=schema)

# x-methods — read definition, runtime logic is yours
user.get_extensions()   # {'x-methods': {'is_adult': {...}}, 'x-docs': {...}}

Validation

from schema2object import ObjectTree

schema = {
    "properties": {
        "email": {"type": "string", "format": "email"},
        "age":   {"type": "integer", "minimum": 0}
    },
    "required": ["email"]
}

# Valid
user = ObjectTree({"email": "alice@example.com", "age": 30}, schema=schema)

# Invalid — raises TypeError
try:
    bad = ObjectTree({"email": "not-an-email"}, schema=schema)
    bad.validate()
except TypeError as e:
    print(e)   # 'email': does not match format 'email'

Schema as Object Class

ObjectTree treats the schema as a class definition:

# Schema = class spec
schema = {
    "properties": {
        "name":  {"type": "string", "minLength": 1},
        "score": {"type": "number", "minimum": 0, "maximum": 100}
    },
    "required": ["name"],
    "x-methods": {
        "passed": {"properties": {"score": {"minimum": 60}}}
    },
    "x-docs": {
        "intent": "Student result entity. Score 60+ means passed."
    }
}

# Instance = ObjectTree
student = ObjectTree({"name": "Alice", "score": 85}, schema=schema)

# Access state
student.name    # "Alice"
student.score   # 85

# x-methods — read definition, runtime logic is yours
student.get_extensions()   # {'x-methods': {'passed': {...}}, 'x-docs': {...}}

API Summary

ObjectTree(data, *, schema=None)   # construct
obj.key                            # dot-access field (attribute)
obj["key"]                         # bracket-access field
obj.get("key")                     # safe access → ObjectTree or None
obj.to_dict()                      # batch output — serialization, API response, writing to disk
obj.validate()                     # validate against schema, raises TypeError on fail

# Draft-07 logic methods
obj.one_of()        # → ObjectTree (XOR branch)
obj.any_of()        # → list[ObjectTree] (OR branches)
obj.all_of()        # → ObjectTree (AND merged schema)
obj.not_of()        # → bool
obj.if_then()       # → ObjectTree (conditional branch)
obj.project()       # → ObjectTree (schema-defined fields only)
obj.contains(schema)# → bool

References

  • Source: projects/schema2object/
  • Philosophy: methodology/sdd-philosophy.md
  • x- extensions: methodology/x-extensions.md
  • Rust equivalent: references/rust/schema-value.md