Skip to content

Serialization API

For conceptual background on JSL's serialization design, see Architecture: Serialization.

Overview

The JSL serialization API provides functions for converting JSL code and data structures to and from JSON representations using content-addressable storage. This approach elegantly handles circular references and ensures efficient serialization of complex object graphs including closures and environments.

Core Functions

JSL Serialization - JSON serialization for JSL values and closures

This module handles the serialization and deserialization of JSL values, including closures with their captured environments. Uses content-addressable storage to handle circular references elegantly.

serialize(obj)

Serialize a JSL value to JSON string.

Parameters:

Name Type Description Default
obj Any

The JSL value to serialize

required

Returns:

Type Description
str

JSON string representation

Source code in jsl/serialization.py
def serialize(obj: Any) -> str:
    """
    Serialize a JSL value to JSON string.

    Args:
        obj: The JSL value to serialize

    Returns:
        JSON string representation
    """
    serializer = ContentAddressableSerializer()
    return serializer.serialize(obj)

deserialize(json_str, prelude_env=None)

Deserialize a JSON string to JSL value.

Parameters:

Name Type Description Default
json_str str

JSON string to deserialize

required
prelude_env Env

Optional prelude environment for closure reconstruction

None

Returns:

Type Description
Any

Reconstructed JSL value

Source code in jsl/serialization.py
def deserialize(json_str: str, prelude_env: Env = None) -> Any:
    """
    Deserialize a JSON string to JSL value.

    Args:
        json_str: JSON string to deserialize
        prelude_env: Optional prelude environment for closure reconstruction

    Returns:
        Reconstructed JSL value
    """
    deserializer = ContentAddressableDeserializer(prelude_env)
    return deserializer.deserialize(json_str)

to_json(obj)

Convert JSL value to JSON-compatible dictionary.

Parameters:

Name Type Description Default
obj Any

JSL value to convert

required

Returns:

Type Description
Dict

JSON-compatible dictionary

Source code in jsl/serialization.py
def to_json(obj: Any) -> Dict:
    """
    Convert JSL value to JSON-compatible dictionary.

    Args:
        obj: JSL value to convert

    Returns:
        JSON-compatible dictionary
    """
    serialized = serialize(obj)
    return json.loads(serialized)

from_json(json_data, prelude_env=None)

Reconstruct JSL value from JSON data.

Parameters:

Name Type Description Default
json_data Any

JSON string or dictionary

required
prelude_env Env

Optional prelude environment

None

Returns:

Type Description
Any

Reconstructed JSL value

Source code in jsl/serialization.py
def from_json(json_data: Any, prelude_env: Env = None) -> Any:
    """
    Reconstruct JSL value from JSON data.

    Args:
        json_data: JSON string or dictionary
        prelude_env: Optional prelude environment

    Returns:
        Reconstructed JSL value
    """
    if isinstance(json_data, str):
        return deserialize(json_data, prelude_env)
    else:
        # Convert dict back to JSON string and deserialize
        json_str = json.dumps(json_data)
        return deserialize(json_str, prelude_env)

serialize_program(program, prelude_hash=None)

Serialize a complete JSL program with metadata.

Parameters:

Name Type Description Default
program Any

The JSL program to serialize

required
prelude_hash str

Optional hash of the prelude version

None

Returns:

Type Description
Dict

Dictionary with program and metadata

Source code in jsl/serialization.py
def serialize_program(program: Any, prelude_hash: str = None) -> Dict:
    """
    Serialize a complete JSL program with metadata.

    Args:
        program: The JSL program to serialize
        prelude_hash: Optional hash of the prelude version

    Returns:
        Dictionary with program and metadata
    """
    return {
        "version": "0.1.0",
        "prelude_hash": prelude_hash,
        "program": to_json(program),
        "timestamp": None  # Could add timestamp if needed
    }

deserialize_program(program_data, prelude_env=None)

Deserialize a complete JSL program.

Parameters:

Name Type Description Default
program_data Dict

Serialized program data

required
prelude_env Env

Prelude environment for reconstruction

None

Returns:

Type Description
Any

Reconstructed JSL program

Source code in jsl/serialization.py
def deserialize_program(program_data: Dict, prelude_env: Env = None) -> Any:
    """
    Deserialize a complete JSL program.

    Args:
        program_data: Serialized program data
        prelude_env: Prelude environment for reconstruction

    Returns:
        Reconstructed JSL program
    """
    # Could add version checking here
    program = program_data.get("program")
    return from_json(program, prelude_env)

Usage Examples

Basic Serialization

from jsl.serialization import serialize, deserialize

# Serialize simple JSL values
expr = ["+", 1, 2]
json_str = serialize(expr)
# Result: '["+", 1, 2]'

# Deserialize back to JSL
restored = deserialize(json_str)
# Result: ["+", 1, 2]

# Complex values use content-addressable format
from jsl import eval_expression, make_prelude
closure = eval_expression('["lambda", ["x"], ["+", "x", 1]]', make_prelude())
serialized = serialize(closure)
# Result contains "__cas_version__", "root", and "objects" fields

Closure Serialization

from jsl import eval_expression, make_prelude, serialize, deserialize

# Create and serialize a closure with captured environment
program = '''
["do",
  ["def", "base", 100],
  ["lambda", ["x"], ["+", "x", "base"]]
]
'''
closure = eval_expression(program, make_prelude())
serialized = serialize(closure)

# Deserialize with prelude environment
restored = deserialize(serialized, make_prelude())

Network Transmission

import json
from jsl.serialization import serialize

# Prepare JSL code for network transmission
code = ["lambda", ["x"], ["*", "x", "x"]]
payload = {
    "type": "execute",
    "code": serialize(code),
    "timestamp": "2023-12-01T10:00:00Z"
}

# Send as JSON
json_payload = json.dumps(payload)

Program Serialization

from jsl.serialization import serialize_program, deserialize_program

# Serialize complete program with metadata
program = ["+", 1, 2, 3]
program_data = serialize_program(program, prelude_hash="v1.0")

# Result includes version, prelude_hash, and program data
# {
#   "version": "0.1.0",
#   "prelude_hash": "v1.0", 
#   "program": 6,
#   "timestamp": null
# }

# Deserialize program
restored = deserialize_program(program_data)

Type Mappings

JSL Type JSON Type Notes
Number Number Direct mapping for primitives
String String Direct mapping for primitives
Boolean Boolean Direct mapping for primitives
Null Null Direct mapping for primitives
Array Array Direct for simple arrays, CAS for arrays containing closures
Object Object Direct for simple objects, CAS for objects containing closures
Closure Object CAS format with __type__: "closure"
Environment Object CAS format with __type__: "env"

Serialization Formats

JSL uses two serialization formats depending on the complexity of the data:

  1. Direct JSON: For primitive values and simple structures without closures
  2. Content-Addressable Storage (CAS): For complex objects containing closures or environments
# Direct JSON format
serialize(42)              # "42"
serialize([1, 2, 3])      # "[1,2,3]"
serialize({"key": "val"}) # "{\"key\":\"val\"}"

# CAS format (contains closures/environments)
serialize(some_closure)   # {"__cas_version__": 1, "root": {...}, "objects": {...}}

Error Handling

The serialization API handles various error conditions:

  • Circular References: Handled elegantly using content-addressable storage
  • Invalid JSON: Proper error messages for malformed input
  • Type Errors: Clear indication of unsupported types
  • Encoding Issues: UTF-8 handling for international text
  • Missing Objects: Validation of object references during deserialization

Performance Notes

  • Time Complexity: O(n) where n is the size of the data structure
  • Space Complexity: Efficient sharing of identical objects through content addressing
  • Circular Reference Handling: No stack overflow or infinite loops
  • Deterministic Hashing: Same content always produces same hash