lightweight utility for JSON serialization
Project description
Jsonic
Jsonic is a lightweight, Pythonic library for effortless JSON serialization and deserialization of Python objects. Built for modern Python with type hints, dataclasses, and developer experience in mind.
from jsonic import serialize, deserialize
from dataclasses import dataclass
from datetime import datetime
@dataclass
class User:
name: str
email: str
created_at: datetime
user = User("Alice", "alice@example.com", datetime.now())
json_data = serialize(user) # Dict or JSON string
user_copy = deserialize(json_data, expected_type=User) # Type-safe!
โจ Features
- ๐ฏ Zero Configuration - Works with dataclasses, type hints, and regular classes
- ๐ Type Safe - Full type hint support with validation
- ๐ Modern Python - Built for Python 3.8+ with dataclasses and type hints
- ๐ฆ Rich Types - Supports tuples, sets, enums, datetime,
__slots__, and more - ๐จ Flexible - Custom serializers, transient fields, private attribute control
- ๐ Partial Serialization - Include/exclude fields with nested dot notation
- ๐ค Pydantic Integration - Seamless support for Pydantic models and field aliases
- ๐ Great Errors - Detailed error messages with exact path to the problem
- โก Fast - Minimal overhead, optimized for performance
- ๐งช Well Tested - 448 tests with >95% coverage
๐ฆ Installation
pip install py-jsonic
Requirements: Python 3.8+
๐ Quick Start
Basic Usage
from jsonic import serialize, deserialize
from dataclasses import dataclass
@dataclass
class Product:
name: str
price: float
in_stock: bool
# Serialize to dict
product = Product("Laptop", 999.99, True)
data = serialize(product)
# {'name': 'Laptop', 'price': 999.99, 'in_stock': True, '_serialized_type': 'Product'}
# Deserialize back to object
product_copy = deserialize(data, expected_type=Product)
assert product.name == product_copy.name
With Nested Objects
from dataclasses import dataclass
from typing import List
@dataclass
class Address:
street: str
city: str
@dataclass
class User:
name: str
addresses: List[Address]
user = User("Bob", [Address("123 Main St", "NYC")])
data = serialize(user)
user_copy = deserialize(data, expected_type=User)
JSON String Output
json_string = serialize(user, string_output=True)
user_copy = deserialize(json_string, string_input=True, expected_type=User)
๐ Core Concepts
Supported Types
Jsonic automatically handles:
- Primitives:
int,float,str,bool,None - Collections:
list,dict,tuple,set - Standard Library:
datetime,Enum,UUID - Python Classes: dataclasses, classes with type hints,
__slots__ - Custom Types: Via
@jsonic_serializerand@jsonic_deserializer
Three Ways to Use Jsonic
1. Dataclasses (Recommended)
from dataclasses import dataclass
from jsonic import serialize, deserialize
@dataclass
class Person:
name: str
age: int
person = Person("Alice", 30)
data = serialize(person)
2. Serializable Base Class
from jsonic import Serializable
from datetime import datetime
class User(Serializable):
def __init__(self, username: str, created_at: datetime):
super().__init__()
self.username = username
self.created_at = created_at
user = User("alice", datetime.now())
data = serialize(user)
3. Register External Classes
from jsonic import register_jsonic_type
class ThirdPartyClass:
def __init__(self, value: str):
self.internal_value = value
# Register with parameter mapping
register_jsonic_type(
ThirdPartyClass,
init_parameters_mapping={'value': 'internal_value'}
)
๐ฏ Common Use Cases
API Responses
@dataclass
class APIResponse:
status: str
data: dict
timestamp: datetime
response = APIResponse("success", {"user_id": 123}, datetime.now())
return serialize(response, string_output=True)
Database Models
@dataclass
class BlogPost:
title: str
content: str
author: User
tags: List[str]
published_at: datetime
# Save to database
post_json = serialize(post, string_output=True)
db.save(post_json)
# Load from database
post_data = db.load()
post = deserialize(post_data, string_input=True, expected_type=BlogPost)
Configuration Files
@dataclass
class AppConfig:
database_url: str
api_keys: dict
features: List[str]
# Load config
with open('config.json') as f:
config = deserialize(json.load(f), expected_type=AppConfig)
Microservices Communication
@dataclass
class OrderEvent:
order_id: str
items: List[Product]
total: float
created_at: datetime
# Send event
event = OrderEvent("ORD-123", products, 299.99, datetime.now())
message_queue.publish(serialize(event, string_output=True))
# Receive event
data = message_queue.consume()
event = deserialize(data, string_input=True, expected_type=OrderEvent)
๐ง Advanced Features
Partial Serialization
Control which fields to serialize with include and exclude parameters:
@dataclass
class User:
username: str
email: str
password_hash: str
api_token: str
user = User("alice", "alice@example.com", "hash123", "token456")
# Exclude sensitive fields
safe_data = serialize(user, exclude={'password_hash', 'api_token'})
# Result: {'username': 'alice', 'email': 'alice@example.com', ...}
# Include only specific fields
public_data = serialize(user, include={'username', 'email'})
# Result: {'username': 'alice', 'email': 'alice@example.com', ...}
Nested field filtering with dot notation:
@dataclass
class Credentials:
username: str
password: str
@dataclass
class Database:
host: str
credentials: Credentials
@dataclass
class Config:
app_name: str
database: Database
config = Config("MyApp", Database("localhost", Credentials("admin", "secret")))
# Exclude nested password field
safe_config = serialize(config, exclude={'database.credentials.password'})
# Password is excluded, but username remains
# Include only specific nested fields
public_config = serialize(config, include={'app_name', 'database.host'})
# Only app_name and database.host are included
Filter fields in lists:
@dataclass
class Item:
name: str
price: float
internal_cost: float
@dataclass
class Order:
order_id: str
items: List[Item]
order = Order("ORD-123", [Item("Widget", 10.0, 5.0)])
# Exclude internal_cost from all items
public_order = serialize(order, exclude={'items.internal_cost'})
Transient Attributes
Exclude fields from serialization at the class level:
class User(Serializable):
transient_attributes = ['password_hash', '_cache']
def __init__(self, username: str, password_hash: str):
super().__init__()
self.username = username
self.password_hash = password_hash # Won't be serialized
self._cache = {} # Won't be serialized
Private Attributes
Control private attribute serialization:
# Exclude private attributes (default)
data = serialize(obj, serialize_private_attributes=False)
# Include private attributes
data = serialize(obj, serialize_private_attributes=True)
Custom Serializers
from jsonic import jsonic_serializer, jsonic_deserializer
from decimal import Decimal
@jsonic_serializer(Decimal)
def serialize_decimal(obj: Decimal) -> dict:
return {'value': str(obj), '_serialized_type': 'Decimal'}
@jsonic_deserializer('Decimal')
def deserialize_decimal(data: dict) -> Decimal:
return Decimal(data['value'])
Type Safety
# Validates the deserialized type matches expected type
user = deserialize(data, expected_type=User)
# Raises error if type doesn't match
try:
product = deserialize(user_data, expected_type=Product)
except TypeError as e:
print(f"Type mismatch: {e}")
Enums
from enum import Enum
class Status(Enum):
PENDING = "pending"
APPROVED = "approved"
REJECTED = "rejected"
@dataclass
class Request:
status: Status
request = Request(Status.APPROVED)
data = serialize(request) # Handles enums automatically
Tuples and Sets
@dataclass
class Data:
coordinates: tuple # (x, y, z)
unique_ids: set
data = Data((1.0, 2.0, 3.0), {1, 2, 3})
serialized = serialize(data) # Preserves tuple and set types
restored = deserialize(serialized, expected_type=Data)
assert isinstance(restored.coordinates, tuple)
assert isinstance(restored.unique_ids, set)
Pydantic Models
Jsonic seamlessly integrates with Pydantic models:
from pydantic import BaseModel, Field
class User(BaseModel):
name: str
email: str
age: int = Field(ge=0, le=150)
nickname: str = Field(alias="display_name")
# Serialize Pydantic models
user = User(name="Alice", email="alice@example.com", age=30, display_name="Ally")
data = serialize(user) # Respects field aliases
# Deserialize to Pydantic models
user_copy = deserialize(data, expected_type=User) # Full validation
Pydantic features supported:
- Auto-detection of Pydantic models
- Field aliases (
alias,validation_alias,serialization_alias) - Nested Pydantic models
- Pydantic validators run on deserialization
๐ Error Handling
Jsonic provides detailed error messages with exact paths:
@dataclass
class Address:
street: str
city: str
@dataclass
class User:
name: str
address: Address
# Error shows exact location
try:
data = {'name': 'Alice', 'address': {'street': 123}} # Wrong type
user = deserialize(data, expected_type=User)
except Exception as e:
print(e)
# DeserializationError: Type mismatch at path: obj.address.street
Type Not Found Suggestions
try:
deserialize({'_serialized_type': 'Usr'}) # Typo
except TypeError as e:
print(e)
# Could not find type: Usr
# Did you mean one of these?
# - User
# - UserProfile
๐ Comparison with Alternatives
| Feature | Jsonic | Pydantic | marshmallow | dataclasses-json |
|---|---|---|---|---|
| Zero config for dataclasses | โ | โ | โ | โ |
| Type hints support | โ | โ | โ ๏ธ | โ |
| Pydantic integration | โ | N/A | โ | โ |
| Partial serialization | โ | โ ๏ธ | โ ๏ธ | โ |
| Nested field filtering | โ | โ | โ | โ |
| Validation | โ ๏ธ | โ | โ | โ |
| Tuples/Sets support | โ | โ ๏ธ | โ | โ |
__slots__ support |
โ | โ | โ | โ |
| Custom serializers | โ | โ | โ | โ ๏ธ |
| Error messages with paths | โ | โ | โ ๏ธ | โ |
| Learning curve | Low | Medium | High | Low |
| Performance | Fast | Fast | Medium | Fast |
When to use Jsonic:
- You want simple, Pythonic serialization without schemas
- You're working with existing classes you can't modify
- You need
__slots__, tuples, or sets support - You want great error messages out of the box
- You need fine-grained control over serialization (include/exclude nested fields)
- You want to work with both dataclasses and Pydantic models
When to use alternatives:
- Pydantic: You need extensive validation and FastAPI integration (but Jsonic can serialize Pydantic models!)
- marshmallow: You need complex validation rules and transformations
- dataclasses-json: You only need basic dataclass serialization
๐ Examples
See EXAMPLES.md for comprehensive examples including:
- Real-world API integration
- Database persistence patterns
- Microservices communication
- Configuration management
- Migration from other libraries
๐ค Contributing
Contributions are welcome! See CONTRIBUTING.md for:
- Development setup
- Running tests
- Code style guidelines
- Pull request process
๐ Documentation
- Examples - Real-world usage examples
- API Reference - Detailed API documentation
- Roadmap - Planned features and timeline
- Contributing - How to contribute
๐ Known Limitations
Jsonic is designed for data classes and may not work with:
- Classes with temporary constructor parameters - Parameters not stored as attributes
- Classes with positional-only parameters - Can't reconstruct with keyword args
- Classes with complex
*args/**kwargs- May not deserialize correctly - Classes with side effects in
__init__- May behave differently on deserialization
For these cases, consider using custom serializers or restructuring your classes.
๐ License
MIT License - see LICENSE file for details.
๐ Acknowledgments
Inspired by the Python community's need for simple, Pythonic serialization that "just works" with modern Python features.
๐ฎ Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: orrbenyamini@gmail.com
Made with โค๏ธ for the Python community
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_jsonic-1.0.1.tar.gz.
File metadata
- Download URL: py_jsonic-1.0.1.tar.gz
- Upload date:
- Size: 64.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
714bae343a7e2b6011239a24ad8318ac944aec7d62290677034821ffa3e6ab20
|
|
| MD5 |
e4302fb7d7f2a0de5ffad54d5135f8ed
|
|
| BLAKE2b-256 |
1c84223df2c4537b877175676460bb21b348d12f2ed9f1f5d990a5172fd0293e
|
File details
Details for the file py_jsonic-1.0.1-py3-none-any.whl.
File metadata
- Download URL: py_jsonic-1.0.1-py3-none-any.whl
- Upload date:
- Size: 76.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
534ac726f0d82122f8796cbbf0fcd8ca82385aae3f7be297243b4b4e1dbdde3b
|
|
| MD5 |
7877d9ee57929afc3dc85347806225a0
|
|
| BLAKE2b-256 |
f86c1e535045838664348fb3fb301764a67881c2de047d6b6fbab228f2f62cf2
|