A comprehensive Python library for serializing and deserializing Python objects to and from JSON (dictionaries) with minimal code changes required.
81
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.
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
"""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
"""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
"""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
"""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'}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.0import 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}")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'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)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-jsonsdocs
evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
scenario-9
scenario-10