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 comprehensive support for dataclasses, enums, datetime objects, collections, and extended standard library types with blazing fast performance! ⚡
✨ Features
| Feature | Description |
|---|---|
| 🚀 High Performance | Introspected type caching and optimized conversion algorithms |
| 🎯 Type Safety | PEP 561 compliant (py.typed) with full static analysis support (mypy --strict) |
| 📦 Dataclasses & Modern Typing | Full support for dataclass, NamedTuple, TypedDict, Literal, and intelligent Union matching |
| 🧩 Extended Types | Native serialization/deserialization for UUID, Decimal, Path, bytes, IP addresses/networks, timedelta, and regex Pattern |
| 📝 Direct String & Stream I/O | dumps() and loads() for strings; dump_stream() and load_stream() for files/network/StringIO |
| 🏷️ Field Metadata | Fine-grained dataclass control: alias, exclude, and per-field custom serializer / deserializer |
| 🔌 Custom Registry | Extensible @serializer and @deserializer decorators, functional APIs, and isolated Registry instances |
| 🛡️ Strict Mode & Error Paths | Enforce exact schema match with strict=True, pinpoint errors with attribute paths (e.g. user.address.zip) |
| 📁 File & Compression | Direct file I/O with automatic .gz gzip compression/decompression |
| 🔧 Zero Dependencies | Pure Python implementation with zero third-party dependencies |
🚀 Quick Start
Installation
pip install jsonport
1. 1.x Backwards Compatible Usage (Dict / Primitive Conversion)
JsonPort 2.0 maintains 100% backward compatibility with all 1.x functions:
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from jsonport import dump, load, dump_file, load_file
class UserRole(Enum):
ADMIN = "admin"
USER = "user"
@dataclass
class User:
name: str
age: int
role: UserRole
created_at: datetime
tags: list[str]
user = User(
name="John Doe",
age=30,
role=UserRole.ADMIN,
created_at=datetime.now(),
tags=["developer", "python"],
)
# Serialize to dictionary/primitives
data = dump(user)
print(data["role"]) # "admin"
# Deserialize back to dataclass
restored_user = load(data, User)
assert restored_user.name == "John Doe"
# Save & load directly from JSON or gzipped files
dump_file(user, "user.json.gz")
loaded_user = load_file("user.json.gz", User)
2. JsonPort 2.0: Direct String & Stream Operations
import io
from jsonport import dumps, loads, dump_stream, load_stream
# Direct JSON string serialization and deserialization
json_str = dumps(user, indent=2)
user_from_str = loads(json_str, User)
# Stream operations with StringIO, files, or network streams
stream = io.StringIO()
dump_stream(user, stream)
stream.seek(0)
user_from_stream = load_stream(stream, User)
3. JsonPort 2.0: Extended Types & Field Metadata
import uuid
import decimal
import pathlib
import ipaddress
from dataclasses import dataclass, field
from jsonport import dumps, loads
@dataclass
class ServerNode:
id: uuid.UUID
rate: decimal.Decimal
config_path: pathlib.Path
ip: ipaddress.IPv4Address
# Field aliases and exclusions
secret_token: str = field(metadata={"exclude": True})
display_name: str = field(metadata={"alias": "server_name"})
node = ServerNode(
id=uuid.uuid4(),
rate=decimal.Decimal("99.99"),
config_path=pathlib.Path("/etc/server.conf"),
ip=ipaddress.IPv4Address("192.168.1.1"),
secret_token="super-secret",
display_name="Node-Alpha",
)
json_data = dumps(node)
# {"id": "...", "rate": "99.99", "config_path": "/etc/server.conf", "ip": "192.168.1.1", "server_name": "Node-Alpha"}
restored_node = loads(json_data, ServerNode)
4. JsonPort 2.0: Custom Registry
from jsonport import serializer, deserializer, dumps, loads
class Money:
def __init__(self, amount: float, currency: str):
self.amount = amount
self.currency = currency
@serializer(Money)
def serialize_money(m: Money) -> str:
return f"{m.currency} {m.amount:.2f}"
@deserializer(Money)
def deserialize_money(s: str) -> Money:
curr, amt = s.split()
return Money(float(amt), curr)
json_str = dumps(Money(49.95, "USD"))
# '"USD 49.95"'
money = loads(json_str, Money)
5. JsonPort 2.0: Strict Mode & Detailed Error Paths
from jsonport import loads, DeserializationError
raw_json = '{"name": "Alice", "unexpected_field": 123}'
try:
loads(raw_json, User, strict=True)
except DeserializationError as err:
print(err)
# Output: Extra keys not permitted in strict mode for User: ['unexpected_field']
print(err.target_type) # <class 'User'>
🐍 Python Version Support
JsonPort 2.0 supports modern Python versions:
| Version | Status |
|---|---|
| Python 3.9 | ✅ Full Support |
| Python 3.10 | ✅ Full Support |
| Python 3.11 | ✅ Full Support |
| Python 3.12 | ✅ Full Support |
| Python 3.13 | ✅ Full Support |
📚 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")
🧪 Testing
JsonPort uses pytest for all automated tests. To run the test suite:
Install Test Dependencies
pip install -e ".[test]"
Run Tests
# All tests
pytest -v
# With coverage
pytest --cov
# Only fast unit tests
pytest -m 'not slow and not integration' -v
# Only performance tests
pytest -m slow -v
# Only integration tests
pytest -m integration -v
Benchmarking
# Run performance benchmarks
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
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
📖 API Reference
Core Functions
| Function | Description | Parameters | Returns |
|---|---|---|---|
dump(obj) |
Serialize object to JSON-serializable format | obj: Object to serialize |
JSON-serializable data |
load(data, target_class) |
Deserialize JSON data to Python object | data: Data to deserializetarget_class: Target class |
Instance of target class |
dump_file(obj, path, overwrite=True) |
Serialize object and save to file | obj: Object to serializepath: File pathoverwrite: Overwrite existing file |
None |
load_file(path, target_class) |
Load JSON file and deserialize | path: File pathtarget_class: Target class |
Instance of target class |
Supported Types
| Category | 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
🤝 Contributing
We welcome contributions! Here's how you can help:
- 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
Development Setup
# Clone the repository
git clone https://github.com/Luan1Schons/JsonPort.git
cd JsonPort
# Install in development mode
pip install -e ".[dev,test]"
# Run tests
pytest -v
# Format code
black jsonport/ tests/
# Check code quality
flake8 jsonport/ tests/ --max-line-length=88 --extend-ignore=E203,W503,E501,F401,F811,F841,E731
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🆘 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! 🚀
Metadata
Release files for jsonport 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| jsonport-2.0.0.tar.gz | 35.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jsonport-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 56.9 kB
Release files / jsonport-2.0.0.tar.gz
| Download URL | jsonport-2.0.0.tar.gz |
|---|---|
| Size | 35.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ba3f05dfc921043c1dc788d4069de849ebd7f68e369d0475bc1fc1df98bb11f3
|
|
BLAKE2b-256 checksum How to use checksums |
55e5dbf375cd1307a826dde6d24ed887dbfcf8084b5725e5eb7a05d3475ce588
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|
Release files / jsonport-2.0.0-py3-none-any.whl
| Download URL | jsonport-2.0.0-py3-none-any.whl |
|---|---|
| Size | 21.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2acf40ea088954d457d148532ea882bfe35e829360b8569a118c6c864d418b9b
|
|
BLAKE2b-256 checksum How to use checksums |
e8d2ee23969aff0d639f7a3268c24459d8193347224850e760a1eda20f744486
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.16
|