A high-performance Python library for serialization and deserialization of Python objects to and from JSON. Supports dataclasses, enums, datetime, collections, and more.
Project description
JsonPort
A high-performance Python library for seamless serialization and deserialization of complex Python objects to/from JSON format. JsonPort provides intelligent type handling, caching optimizations, and support for dataclasses, enums, datetime objects, and collections.
Features
- 🚀 High Performance: Optimized with caching for type hints and optional type resolution
- 🎯 Type Safety: Full type hints support with automatic type conversion
- 📦 Dataclass Support: Native serialization/deserialization of dataclasses
- 🗓️ DateTime Handling: Automatic ISO format conversion for datetime objects
- 🔄 Collection Support: Lists, tuples, sets, and dictionaries with type preservation
- 📁 File Operations: Direct file I/O with gzip compression support
- 🎨 Enum Support: Automatic enum value serialization
- 🛡️ Error Handling: Comprehensive error messages and validation
Installation
pip install jsonport
Python Version Support
JsonPort supports the following Python versions:
- Python 3.7 - Full support with all features (EOL - End of Life)
- Python 3.8 - Full support with all features
- Python 3.9 - Full support with all features
- Python 3.10 - Full support with all features
- Python 3.11 - Full support with all features
- Python 3.12 - Full support with all features
- Python 3.13 - Full support with all features
All features including dataclasses, type hints, datetime handling, and file operations work consistently across all supported Python versions.
Note: Python 3.7 reached End of Life (EOL) in June 2023. While JsonPort still supports Python 3.7 for compatibility with existing projects, we recommend upgrading to Python 3.8+ for new projects. The CI/CD pipeline tests Python 3.8+ due to GitHub Actions limitations.
Quick Start
Basic Usage
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from jsonport import dump, load, dump_file, load_file
# Define your data structures
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
@dataclass
class User:
name: str
age: int
role: UserRole
created_at: datetime
tags: list[str]
# Create an instance
user = User(
name="John Doe",
age=30,
role=UserRole.ADMIN,
created_at=datetime.now(),
tags=["developer", "python"]
)
# Serialize to dictionary
data = dump(user)
print(data)
# Output:
# {
# "name": "John Doe",
# "age": 30,
# "role": "admin",
# "created_at": "2025-07-14T10:30:00",
# "tags": ["developer", "python"]
# }
# Deserialize back to object
restored_user = load(data, User)
print(restored_user.name) # "John Doe"
File Operations
# Save to JSON file
dump_file(user, "user.json")
# Load from JSON file
loaded_user = load_file("user.json", User)
# Save with compression
dump_file(user, "user.json.gz")
# Load compressed file
compressed_user = load_file("user.json.gz", User)
Advanced Examples
Complex Nested Structures
from dataclasses import dataclass
from typing import List, Dict, Optional, Tuple
from datetime import date
@dataclass
class Address:
street: str
city: str
country: str
postal_code: str
@dataclass
class Contact:
email: str
phone: Optional[str] = None
@dataclass
class Company:
name: str
founded: date
employees: int
address: Address
contacts: List[Contact]
departments: Dict[str, List[str]]
# Create complex object
company = Company(
name="TechCorp",
founded=date(2020, 1, 1),
employees=150,
address=Address(
street="123 Tech Street",
city="San Francisco",
country="USA",
postal_code="94105"
),
contacts=[
Contact("info@techcorp.com"),
Contact("support@techcorp.com", "+1-555-0123")
],
departments={
"Engineering": ["Backend", "Frontend", "DevOps"],
"Sales": ["Enterprise", "SMB"],
"Marketing": ["Digital", "Content"]
}
)
# Serialize complex structure
data = dump(company)
# Deserialize with full type preservation
restored_company = load(data, Company)
Collections with Type Information
from dataclasses import dataclass
from typing import Set, Tuple
@dataclass
class Product:
id: int
name: str
price: float
categories: Set[str]
dimensions: Tuple[float, float, float]
product = Product(
id=1,
name="Laptop",
price=999.99,
categories={"electronics", "computers", "portable"},
dimensions=(35.5, 24.0, 2.1)
)
# Serialize with collection type preservation
data = dump(product)
# Sets are converted to lists, tuples preserved
print(data["categories"]) # ["electronics", "computers", "portable"]
print(data["dimensions"]) # [35.5, 24.0, 2.1]
# Deserialize with proper type restoration
restored_product = load(data, Product)
print(type(restored_product.categories)) # <class 'set'>
print(type(restored_product.dimensions)) # <class 'tuple'>
Custom JSON Encoder
import json
from jsonport import JsonPortEncoder
# Use the custom encoder with standard json module
data = dump(company)
json_string = json.dumps(data, cls=JsonPortEncoder, indent=2)
print(json_string)
Error Handling
from jsonport import JsonPortError
try:
# Try to serialize non-serializable object
non_serializable = lambda x: x
dump(non_serializable)
except JsonPortError as e:
print(f"Serialization error: {e}")
try:
# Try to load file that doesn't exist
load_file("nonexistent.json", User)
except FileNotFoundError:
print("File not found")
except JsonPortError as e:
print(f"Deserialization error: {e}")
Performance Features
Caching Optimizations
JsonPort automatically caches:
- Type hints for dataclasses (max 1024 entries)
- Optional type resolution (max 512 entries)
This provides significant performance improvements when working with the same dataclass types repeatedly.
Benchmarks
import time
from dataclasses import dataclass
from jsonport import dump, load
@dataclass
class BenchmarkData:
id: int
name: str
values: list[float]
metadata: dict[str, str]
# Create test data
test_data = BenchmarkData(
id=1,
name="test",
values=[1.1, 2.2, 3.3] * 1000,
metadata={"key1": "value1", "key2": "value2"}
)
# Benchmark serialization
start_time = time.time()
for _ in range(1000):
data = dump(test_data)
serialization_time = time.time() - start_time
# Benchmark deserialization
start_time = time.time()
for _ in range(1000):
restored = load(data, BenchmarkData)
deserialization_time = time.time() - start_time
print(f"Serialization: {serialization_time:.4f}s")
print(f"Deserialization: {deserialization_time:.4f}s")
API Reference
Core Functions
dump(obj: Any) -> Any
Serialize a Python object to JSON-serializable format.
Parameters:
obj: Object to serialize (dataclass, list, tuple, set, dict, or primitive)
Returns:
- JSON-serializable dictionary, list, or primitive value
load(data: Any, target_class: Type[T]) -> T
Deserialize JSON data to Python object.
Parameters:
data: Dictionary/list to deserializetarget_class: Target class for deserialization
Returns:
- Instance of the target class
dump_file(obj: Any, path: str, overwrite: bool = True) -> None
Serialize object and save to JSON file.
Parameters:
obj: Object to serializepath: Path to the JSON fileoverwrite: If False, raises error when file exists
load_file(path: str, target_class: Type[T]) -> T
Load JSON file and deserialize to object.
Parameters:
path: Path to the JSON filetarget_class: Target class for deserialization
Returns:
- Instance of the target class
Supported Types
- Primitives:
str,int,float,bool - Datetime:
datetime.datetime,datetime.date,datetime.time - Collections:
list,tuple,set,dict - Custom Types:
dataclass,Enum - Optional Types:
Optional[T],Union[T, None]
Best Practices
1. Use Type Hints
Always define proper type hints for optimal performance and type safety:
@dataclass
class User:
name: str
age: int
email: Optional[str] = None
tags: List[str] = None
2. Handle Optional Fields
Use Optional types for fields that might be None:
@dataclass
class Product:
id: int
name: str
description: Optional[str] = None
price: Optional[float] = None
3. Use Appropriate Collections
Choose the right collection type for your data:
@dataclass
class Configuration:
settings: Dict[str, Any]
allowed_users: Set[str]
coordinates: Tuple[float, float]
items: List[str]
4. Error Handling
Always handle potential errors in production code:
try:
data = load_file("config.json", Config)
except (FileNotFoundError, JsonPortError) as e:
logger.error(f"Failed to load config: {e}")
data = Config() # Use default config
Running the Tests
JsonPort uses pytest for all automated tests. To run the test suite:
- Install test dependencies (only once):
pip install -e ".[test]"
- Run all tests:
pytest -v
- Check code coverage:
pytest --cov
- Run only fast unit tests (skip slow/integration):
pytest -m 'not slow and not integration' -v
- Run only performance tests:
pytest -m slow -v
- Run only integration tests:
pytest -m integration -v
All tests are located in the tests/ directory and cover all library features. If you see any errors, make sure you are using Python 3.7+ and all dependencies are installed.
Benchmarking
JsonPort includes performance tests using pytest-benchmark. To run benchmarks and measure serialization/deserialization speed:
pytest --benchmark-only -v
Example output:
--------------------------------------------------------------------------------------------- benchmark: 2 tests -----------------------------------------------------------------------------
Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
test_deserialization_benchmark 110.3460 (1.0) 263.2940 (1.0) 120.8443 (1.0) 12.3452 (1.0) 118.4470 (1.0) 6.0770 (1.0) 386;464 8.2751 (1.0) 6829 1
test_serialization_benchmark 251.4210 (2.28) 522.7470 (1.99) 270.2584 (2.24) 16.9108 (1.37) 266.3670 (2.25) 12.2920 (2.02) 218;161 3.7002 (0.45) 2499 1
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- OPS (Kops/s): Operations per second (higher is better)
- Mean: Average time per operation
- Rounds/Iterations: Number of repetitions for statistical accuracy
You can compare results between runs or different machines. For more options, see:
pytest --help | grep benchmark
Development and Deployment
Scripts de Deploy
JsonPort inclui scripts automatizados para sincronizar o changelog e publicar no PyPI:
Script Principal (deploy.sh)
# Build apenas (sem publicar)
./deploy.sh
# Build e publicar no PyPI
./deploy.sh --publish
# Build com versão específica
./deploy.sh --version=1.1.0 --publish
Script Interativo (release.sh)
# Verifica mudanças e sugere ações
./release.sh
O que os scripts fazem:
- ✅ Atualizam o changelog automaticamente
- ✅ Executam todos os testes
- ✅ Verificam qualidade do código (black, flake8, mypy)
- ✅ Constroem o pacote
- ✅ Publicam no PyPI (se solicitado)
- ✅ Criam tags e releases no GitHub
- ✅ Sincronizam com o repositório
Configuração necessária:
- Configure
~/.pypirccom suas credenciais do PyPI - Instale dependências:
pip install build twine pytest black flake8 mypy
Para mais detalhes, veja DEPLOY.md.
GitHub Actions
O projeto usa GitHub Actions para:
- CI/CD: Testes automáticos em todas as versões do Python
- Release: Publicação automática no PyPI quando tags são criadas
- Code Quality: Verificação de formatação e tipos
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
Version 1.0.0
- Initial release
- High-performance serialization/deserialization
- Dataclass support with type hints
- File I/O with gzip compression
- Comprehensive error handling
- Caching optimizations for type hints
Support
If you encounter any issues or have questions, please:
- Check the documentation
- Search existing issues
- Create a new issue
JsonPort - Making JSON serialization simple, fast, and type-safe! 🚀
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 jsonport-1.0.2.tar.gz.
File metadata
- Download URL: jsonport-1.0.2.tar.gz
- Upload date:
- Size: 19.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1040156d5eff10d762620d3bc4b686fa41bb997e2bdcc617b1c6ca0fd6d61bda
|
|
| MD5 |
6b945ad97bf897dc1b5946be462bdec9
|
|
| BLAKE2b-256 |
c20d1487a1e2e8b075bee9f00a9f7aa5e1fd4d38ddccaf596234f2a6980ca437
|
File details
Details for the file jsonport-1.0.2-py3-none-any.whl.
File metadata
- Download URL: jsonport-1.0.2-py3-none-any.whl
- Upload date:
- Size: 14.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.10.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d595b099d99f253d261fe6ab1246922aadcf9551c2a67f6e789ac0665240a2a
|
|
| MD5 |
e7e613890967f3a541c8c3000181af06
|
|
| BLAKE2b-256 |
b1fd7b2ae253bce3c8e697090a044b0c58008ddac9f9f4e464e5ae7249b4ae6b
|