CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-jsons

A comprehensive Python library for serializing and deserializing Python objects to and from JSON (dictionaries) with minimal code changes required.

81

1.65x
Overview
Eval results
Files

customization.mddocs/

Customization

Extensible serializer and deserializer system for handling custom types and modifying default behavior. The jsons library allows you to register custom serializers and deserializers, set up validation functions, and control the serialization process through various configuration options.

Capabilities

Serializer Management

def set_serializer(func, cls, high_prio=True, fork_inst=None):
    """
    Set a custom serializer function for the given type(s).
    
    Parameters:
    - func: Callable that takes an object and **kwargs, returns JSON-compatible object
    - cls: Type or sequence of types this serializer can handle
    - high_prio: Bool determining lookup priority (True = higher priority)
    - fork_inst: Optional fork instance for separate serializer configuration
    
    Returns:
    None
    """

def get_serializer(cls, fork_inst=None):
    """
    Get the serializer function that would be used for the given type.
    
    Parameters:
    - cls: Type for which to retrieve the serializer
    - fork_inst: Optional fork instance
    
    Returns:
    Callable: Serializer function for the specified type, or None if not found
    """

Deserializer Management

def set_deserializer(func, cls, high_prio=True, fork_inst=None):
    """
    Set a custom deserializer function for the given type(s).
    
    Parameters:
    - func: Callable that takes a JSON object, cls, and **kwargs, returns instance of cls
    - cls: Type or sequence of types this deserializer can handle  
    - high_prio: Bool determining lookup priority (True = higher priority)
    - fork_inst: Optional fork instance for separate deserializer configuration
    
    Returns:
    None
    """

def get_deserializer(cls, fork_inst=None):
    """
    Get the deserializer function that would be used for the given type.
    
    Parameters:
    - cls: Type for which to retrieve the deserializer
    - fork_inst: Optional fork instance
    
    Returns:
    Callable: Deserializer function for the specified type, or None if not found
    """

Validation System

def set_validator(func, cls, fork_inst=None):
    """
    Set a validator function for the given type(s).
    
    Parameters:
    - func: Callable that takes an instance and returns bool (True if valid)
    - cls: Type or sequence of types this validator can handle
    - fork_inst: Optional fork instance
    
    Returns:
    None
    """

def get_validator(cls, fork_inst=None):
    """
    Get the validator function for the given type.
    
    Parameters:
    - cls: Type for which to retrieve the validator
    - fork_inst: Optional fork instance
    
    Returns:
    Callable: Validator function for the specified type, or None if not found
    """

def validate(obj, cls=None, fork_inst=None, **kwargs):
    """
    Validate an object using the registered validator for its type.
    
    Parameters:
    - obj: Object to validate
    - cls: Optional type to validate obj as (defaults to obj's type)
    - fork_inst: Optional fork instance
    - **kwargs: Additional arguments passed to validator
    
    Returns:
    bool: True if validation passes
    
    Raises:
    - ValidationError: If validation fails
    """

Advanced Utilities

def announce_class(cls, fork_inst=None):
    """
    Announce a class to jsons for improved deserialization performance.
    
    Parameters:
    - cls: Class to announce
    - fork_inst: Optional fork instance
    
    Returns:
    None
    """

Usage Examples

Custom Serializers

import jsons
from datetime import datetime
from dataclasses import dataclass

# Custom serializer for datetime objects to use specific format
def custom_datetime_serializer(dt: datetime, **kwargs) -> str:
    return dt.strftime("%Y-%m-%d %H:%M:%S")

# Register the custom serializer
jsons.set_serializer(custom_datetime_serializer, datetime)

@dataclass
class Event:
    name: str
    timestamp: datetime

event = Event("Meeting", datetime(2023, 12, 1, 14, 30, 0))

# Uses custom datetime format
serialized = jsons.dump(event)
print(serialized)
# {'name': 'Meeting', 'timestamp': '2023-12-01 14:30:00'}

# Custom serializer for multiple types
def uppercase_serializer(obj, **kwargs) -> str:
    return str(obj).upper()

# Apply to multiple string-like types
jsons.set_serializer(uppercase_serializer, (str, bytes))

data = {'message': 'hello world', 'binary': b'test data'}
serialized = jsons.dump(data)
print(serialized)
# {'message': 'HELLO WORLD', 'binary': 'TEST DATA'}

Custom Deserializers

import jsons
from datetime import datetime
from dataclasses import dataclass

# Custom deserializer for datetime from specific format
def custom_datetime_deserializer(json_str: str, cls, **kwargs) -> datetime:
    return datetime.strptime(json_str, "%Y-%m-%d %H:%M:%S")

# Register the custom deserializer
jsons.set_deserializer(custom_datetime_deserializer, datetime)

@dataclass
class Event:
    name: str
    timestamp: datetime

# Can now deserialize from custom format
event_data = {'name': 'Conference', 'timestamp': '2023-12-15 09:00:00'}
event = jsons.load(event_data, Event)
print(event.timestamp)  # datetime object: 2023-12-15 09:00:00
print(type(event.timestamp))  # <class 'datetime.datetime'>


# Custom deserializer for complex object creation
class Temperature:
    def __init__(self, celsius: float):
        self.celsius = celsius
    
    @property  
    def fahrenheit(self):
        return (self.celsius * 9/5) + 32

def temperature_deserializer(json_obj, cls, **kwargs) -> Temperature:
    if isinstance(json_obj, dict):
        if 'celsius' in json_obj:
            return Temperature(json_obj['celsius'])
        elif 'fahrenheit' in json_obj:
            celsius = (json_obj['fahrenheit'] - 32) * 5/9
            return Temperature(celsius)
    elif isinstance(json_obj, (int, float)):
        return Temperature(json_obj)  # Assume celsius
    raise ValueError(f"Cannot deserialize {json_obj} to Temperature")

jsons.set_deserializer(temperature_deserializer, Temperature)

# Can deserialize from various formats
temp1 = jsons.load({'celsius': 25.0}, Temperature)
temp2 = jsons.load({'fahrenheit': 77.0}, Temperature)  
temp3 = jsons.load(30.0, Temperature)

print(temp1.celsius)     # 25.0
print(temp2.celsius)     # 25.0 (converted from fahrenheit)
print(temp3.fahrenheit)  # 86.0

Validation System

import jsons
from jsons import ValidationError
from dataclasses import dataclass
from typing import List

@dataclass
class User:
    username: str
    email: str
    age: int

# Custom validator for User objects
def validate_user(user: User, **kwargs) -> bool:
    if len(user.username) < 3:
        raise ValidationError("Username must be at least 3 characters")
    if '@' not in user.email:
        raise ValidationError("Email must contain @ symbol")
    if user.age < 0 or user.age > 150:
        raise ValidationError("Age must be between 0 and 150")
    return True

# Register the validator
jsons.set_validator(validate_user, User)

# Valid user passes validation
valid_user_data = {'username': 'alice', 'email': 'alice@example.com', 'age': 25}
user = jsons.load(valid_user_data, User)
print(jsons.validate(user))  # True

# Invalid users raise ValidationError
try:
    invalid_user_data = {'username': 'xy', 'email': 'invalid-email', 'age': 25}
    user = jsons.load(invalid_user_data, User)
    jsons.validate(user)  # Raises ValidationError
except ValidationError as e:
    print(f"Validation failed: {e}")

# Validation is automatically called during load by default
try:
    bad_data = {'username': 'bob', 'email': 'bob@example.com', 'age': -5}
    user = jsons.load(bad_data, User)  # Will automatically validate
except ValidationError as e:
    print(f"Load failed validation: {e}")

Priority System and Overriding Defaults

import jsons
from datetime import datetime

# Default datetime serializer produces ISO format
event_time = datetime(2023, 12, 1, 14, 30, 0)
default_serialized = jsons.dump(event_time)
print(default_serialized)  # '2023-12-01T14:30:00'

# Override with low priority (default behavior takes precedence)
def low_prio_serializer(dt: datetime, **kwargs) -> str:
    return "LOW_PRIO_FORMAT"

jsons.set_serializer(low_prio_serializer, datetime, high_prio=False)
still_default = jsons.dump(event_time)
print(still_default)  # '2023-12-01T14:30:00' (still uses default)

# Override with high priority (takes precedence)
def high_prio_serializer(dt: datetime, **kwargs) -> str:
    return dt.strftime("%d/%m/%Y %H:%M")

jsons.set_serializer(high_prio_serializer, datetime, high_prio=True)
custom_format = jsons.dump(event_time)
print(custom_format)  # '01/12/2023 14:30'

Fork-Specific Customization

import jsons
from jsons import JsonSerializable
from dataclasses import dataclass

# Create separate fork for API serialization
APISerializable = JsonSerializable.fork("APIConfig")

# Set up different serializers for different contexts
def api_datetime_serializer(dt: datetime, **kwargs) -> int:
    return int(dt.timestamp())  # Unix timestamp for APIs

def ui_datetime_serializer(dt: datetime, **kwargs) -> str:
    return dt.strftime("%B %d, %Y at %I:%M %p")  # Human-readable for UI

# Configure different forks
jsons.set_serializer(api_datetime_serializer, datetime, fork_inst=APISerializable)
jsons.set_serializer(ui_datetime_serializer, datetime)  # Default fork

@dataclass
class Event:
    name: str
    when: datetime

class APIEvent(APISerializable):
    def __init__(self, name: str, when: datetime):
        self.name = name
        self.when = when

event_time = datetime(2023, 12, 1, 14, 30, 0)

# Regular serialization uses UI format
regular_event = Event("Meeting", event_time)
ui_json = jsons.dump(regular_event)
print(ui_json['when'])  # 'December 01, 2023 at 02:30 PM'

# API serialization uses timestamp format
api_event = APIEvent("Meeting", event_time)
api_json = api_event.json
print(api_json['when'])  # 1701435000 (Unix timestamp)

Complex Custom Type Example

import jsons
from typing import Dict, Any
import base64

class SecureData:
    """Custom class that encrypts data during serialization"""
    
    def __init__(self, data: str, key: str = "default"):
        self.data = data
        self.key = key
    
    def encrypt(self) -> str:
        # Simple base64 encoding for demo (use real encryption in production)
        encoded = base64.b64encode(self.data.encode()).decode()
        return f"{self.key}:{encoded}"
    
    @classmethod
    def decrypt(cls, encrypted: str) -> 'SecureData':
        key, encoded = encrypted.split(':', 1)
        data = base64.b64decode(encoded.encode()).decode()
        return cls(data, key)

# Custom serializer for SecureData
def secure_data_serializer(obj: SecureData, **kwargs) -> str:
    return obj.encrypt()

# Custom deserializer for SecureData  
def secure_data_deserializer(json_str: str, cls, **kwargs) -> SecureData:
    return SecureData.decrypt(json_str)

# Custom validator for SecureData
def secure_data_validator(obj: SecureData, **kwargs) -> bool:
    if not obj.data:
        raise jsons.ValidationError("SecureData cannot have empty data")
    if len(obj.key) < 3:
        raise jsons.ValidationError("SecureData key must be at least 3 characters")
    return True

# Register all custom functions
jsons.set_serializer(secure_data_serializer, SecureData)
jsons.set_deserializer(secure_data_deserializer, SecureData)
jsons.set_validator(secure_data_validator, SecureData)

# Announce the class for better performance
jsons.announce_class(SecureData)

# Usage example
from dataclasses import dataclass

@dataclass
class UserProfile:
    username: str
    sensitive_info: SecureData

profile = UserProfile(
    username="alice",
    sensitive_info=SecureData("Social Security: 123-45-6789", "user_alice_key")
)

# Serialize (data gets encrypted)
serialized = jsons.dump(profile)
print(serialized)
# {
#   'username': 'alice', 
#   'sensitive_info': 'user_alice_key:U29jaWFsIFNlY3VyaXR5OiAxMjMtNDUtNjc4OQ=='
# }

# Deserialize (data gets decrypted)
restored = jsons.load(serialized, UserProfile)
print(restored.sensitive_info.data)  # 'Social Security: 123-45-6789'
print(restored.sensitive_info.key)   # 'user_alice_key'

# Validation works automatically
try:
    invalid_profile = UserProfile("bob", SecureData("", "x"))  # Invalid data
    jsons.validate(invalid_profile)
except jsons.ValidationError as e:
    print(f"Validation failed: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-jsons

docs

class-integration.md

configuration.md

core-serialization.md

customization.md

index.md

key-transformation.md

type-system.md

tile.json