0
# Customization
1
2
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.
3
4
## Capabilities
5
6
### Serializer Management
7
8
```python { .api }
9
def set_serializer(func, cls, high_prio=True, fork_inst=None):
10
"""
11
Set a custom serializer function for the given type(s).
12
13
Parameters:
14
- func: Callable that takes an object and **kwargs, returns JSON-compatible object
15
- cls: Type or sequence of types this serializer can handle
16
- high_prio: Bool determining lookup priority (True = higher priority)
17
- fork_inst: Optional fork instance for separate serializer configuration
18
19
Returns:
20
None
21
"""
22
23
def get_serializer(cls, fork_inst=None):
24
"""
25
Get the serializer function that would be used for the given type.
26
27
Parameters:
28
- cls: Type for which to retrieve the serializer
29
- fork_inst: Optional fork instance
30
31
Returns:
32
Callable: Serializer function for the specified type, or None if not found
33
"""
34
```
35
36
### Deserializer Management
37
38
```python { .api }
39
def set_deserializer(func, cls, high_prio=True, fork_inst=None):
40
"""
41
Set a custom deserializer function for the given type(s).
42
43
Parameters:
44
- func: Callable that takes a JSON object, cls, and **kwargs, returns instance of cls
45
- cls: Type or sequence of types this deserializer can handle
46
- high_prio: Bool determining lookup priority (True = higher priority)
47
- fork_inst: Optional fork instance for separate deserializer configuration
48
49
Returns:
50
None
51
"""
52
53
def get_deserializer(cls, fork_inst=None):
54
"""
55
Get the deserializer function that would be used for the given type.
56
57
Parameters:
58
- cls: Type for which to retrieve the deserializer
59
- fork_inst: Optional fork instance
60
61
Returns:
62
Callable: Deserializer function for the specified type, or None if not found
63
"""
64
```
65
66
### Validation System
67
68
```python { .api }
69
def set_validator(func, cls, fork_inst=None):
70
"""
71
Set a validator function for the given type(s).
72
73
Parameters:
74
- func: Callable that takes an instance and returns bool (True if valid)
75
- cls: Type or sequence of types this validator can handle
76
- fork_inst: Optional fork instance
77
78
Returns:
79
None
80
"""
81
82
def get_validator(cls, fork_inst=None):
83
"""
84
Get the validator function for the given type.
85
86
Parameters:
87
- cls: Type for which to retrieve the validator
88
- fork_inst: Optional fork instance
89
90
Returns:
91
Callable: Validator function for the specified type, or None if not found
92
"""
93
94
def validate(obj, cls=None, fork_inst=None, **kwargs):
95
"""
96
Validate an object using the registered validator for its type.
97
98
Parameters:
99
- obj: Object to validate
100
- cls: Optional type to validate obj as (defaults to obj's type)
101
- fork_inst: Optional fork instance
102
- **kwargs: Additional arguments passed to validator
103
104
Returns:
105
bool: True if validation passes
106
107
Raises:
108
- ValidationError: If validation fails
109
"""
110
```
111
112
### Advanced Utilities
113
114
```python { .api }
115
def announce_class(cls, fork_inst=None):
116
"""
117
Announce a class to jsons for improved deserialization performance.
118
119
Parameters:
120
- cls: Class to announce
121
- fork_inst: Optional fork instance
122
123
Returns:
124
None
125
"""
126
```
127
128
## Usage Examples
129
130
### Custom Serializers
131
132
```python
133
import jsons
134
from datetime import datetime
135
from dataclasses import dataclass
136
137
# Custom serializer for datetime objects to use specific format
138
def custom_datetime_serializer(dt: datetime, **kwargs) -> str:
139
return dt.strftime("%Y-%m-%d %H:%M:%S")
140
141
# Register the custom serializer
142
jsons.set_serializer(custom_datetime_serializer, datetime)
143
144
@dataclass
145
class Event:
146
name: str
147
timestamp: datetime
148
149
event = Event("Meeting", datetime(2023, 12, 1, 14, 30, 0))
150
151
# Uses custom datetime format
152
serialized = jsons.dump(event)
153
print(serialized)
154
# {'name': 'Meeting', 'timestamp': '2023-12-01 14:30:00'}
155
156
# Custom serializer for multiple types
157
def uppercase_serializer(obj, **kwargs) -> str:
158
return str(obj).upper()
159
160
# Apply to multiple string-like types
161
jsons.set_serializer(uppercase_serializer, (str, bytes))
162
163
data = {'message': 'hello world', 'binary': b'test data'}
164
serialized = jsons.dump(data)
165
print(serialized)
166
# {'message': 'HELLO WORLD', 'binary': 'TEST DATA'}
167
```
168
169
### Custom Deserializers
170
171
```python
172
import jsons
173
from datetime import datetime
174
from dataclasses import dataclass
175
176
# Custom deserializer for datetime from specific format
177
def custom_datetime_deserializer(json_str: str, cls, **kwargs) -> datetime:
178
return datetime.strptime(json_str, "%Y-%m-%d %H:%M:%S")
179
180
# Register the custom deserializer
181
jsons.set_deserializer(custom_datetime_deserializer, datetime)
182
183
@dataclass
184
class Event:
185
name: str
186
timestamp: datetime
187
188
# Can now deserialize from custom format
189
event_data = {'name': 'Conference', 'timestamp': '2023-12-15 09:00:00'}
190
event = jsons.load(event_data, Event)
191
print(event.timestamp) # datetime object: 2023-12-15 09:00:00
192
print(type(event.timestamp)) # <class 'datetime.datetime'>
193
194
195
# Custom deserializer for complex object creation
196
class Temperature:
197
def __init__(self, celsius: float):
198
self.celsius = celsius
199
200
@property
201
def fahrenheit(self):
202
return (self.celsius * 9/5) + 32
203
204
def temperature_deserializer(json_obj, cls, **kwargs) -> Temperature:
205
if isinstance(json_obj, dict):
206
if 'celsius' in json_obj:
207
return Temperature(json_obj['celsius'])
208
elif 'fahrenheit' in json_obj:
209
celsius = (json_obj['fahrenheit'] - 32) * 5/9
210
return Temperature(celsius)
211
elif isinstance(json_obj, (int, float)):
212
return Temperature(json_obj) # Assume celsius
213
raise ValueError(f"Cannot deserialize {json_obj} to Temperature")
214
215
jsons.set_deserializer(temperature_deserializer, Temperature)
216
217
# Can deserialize from various formats
218
temp1 = jsons.load({'celsius': 25.0}, Temperature)
219
temp2 = jsons.load({'fahrenheit': 77.0}, Temperature)
220
temp3 = jsons.load(30.0, Temperature)
221
222
print(temp1.celsius) # 25.0
223
print(temp2.celsius) # 25.0 (converted from fahrenheit)
224
print(temp3.fahrenheit) # 86.0
225
```
226
227
### Validation System
228
229
```python
230
import jsons
231
from jsons import ValidationError
232
from dataclasses import dataclass
233
from typing import List
234
235
@dataclass
236
class User:
237
username: str
238
email: str
239
age: int
240
241
# Custom validator for User objects
242
def validate_user(user: User, **kwargs) -> bool:
243
if len(user.username) < 3:
244
raise ValidationError("Username must be at least 3 characters")
245
if '@' not in user.email:
246
raise ValidationError("Email must contain @ symbol")
247
if user.age < 0 or user.age > 150:
248
raise ValidationError("Age must be between 0 and 150")
249
return True
250
251
# Register the validator
252
jsons.set_validator(validate_user, User)
253
254
# Valid user passes validation
255
valid_user_data = {'username': 'alice', 'email': 'alice@example.com', 'age': 25}
256
user = jsons.load(valid_user_data, User)
257
print(jsons.validate(user)) # True
258
259
# Invalid users raise ValidationError
260
try:
261
invalid_user_data = {'username': 'xy', 'email': 'invalid-email', 'age': 25}
262
user = jsons.load(invalid_user_data, User)
263
jsons.validate(user) # Raises ValidationError
264
except ValidationError as e:
265
print(f"Validation failed: {e}")
266
267
# Validation is automatically called during load by default
268
try:
269
bad_data = {'username': 'bob', 'email': 'bob@example.com', 'age': -5}
270
user = jsons.load(bad_data, User) # Will automatically validate
271
except ValidationError as e:
272
print(f"Load failed validation: {e}")
273
```
274
275
### Priority System and Overriding Defaults
276
277
```python
278
import jsons
279
from datetime import datetime
280
281
# Default datetime serializer produces ISO format
282
event_time = datetime(2023, 12, 1, 14, 30, 0)
283
default_serialized = jsons.dump(event_time)
284
print(default_serialized) # '2023-12-01T14:30:00'
285
286
# Override with low priority (default behavior takes precedence)
287
def low_prio_serializer(dt: datetime, **kwargs) -> str:
288
return "LOW_PRIO_FORMAT"
289
290
jsons.set_serializer(low_prio_serializer, datetime, high_prio=False)
291
still_default = jsons.dump(event_time)
292
print(still_default) # '2023-12-01T14:30:00' (still uses default)
293
294
# Override with high priority (takes precedence)
295
def high_prio_serializer(dt: datetime, **kwargs) -> str:
296
return dt.strftime("%d/%m/%Y %H:%M")
297
298
jsons.set_serializer(high_prio_serializer, datetime, high_prio=True)
299
custom_format = jsons.dump(event_time)
300
print(custom_format) # '01/12/2023 14:30'
301
```
302
303
### Fork-Specific Customization
304
305
```python
306
import jsons
307
from jsons import JsonSerializable
308
from dataclasses import dataclass
309
310
# Create separate fork for API serialization
311
APISerializable = JsonSerializable.fork("APIConfig")
312
313
# Set up different serializers for different contexts
314
def api_datetime_serializer(dt: datetime, **kwargs) -> int:
315
return int(dt.timestamp()) # Unix timestamp for APIs
316
317
def ui_datetime_serializer(dt: datetime, **kwargs) -> str:
318
return dt.strftime("%B %d, %Y at %I:%M %p") # Human-readable for UI
319
320
# Configure different forks
321
jsons.set_serializer(api_datetime_serializer, datetime, fork_inst=APISerializable)
322
jsons.set_serializer(ui_datetime_serializer, datetime) # Default fork
323
324
@dataclass
325
class Event:
326
name: str
327
when: datetime
328
329
class APIEvent(APISerializable):
330
def __init__(self, name: str, when: datetime):
331
self.name = name
332
self.when = when
333
334
event_time = datetime(2023, 12, 1, 14, 30, 0)
335
336
# Regular serialization uses UI format
337
regular_event = Event("Meeting", event_time)
338
ui_json = jsons.dump(regular_event)
339
print(ui_json['when']) # 'December 01, 2023 at 02:30 PM'
340
341
# API serialization uses timestamp format
342
api_event = APIEvent("Meeting", event_time)
343
api_json = api_event.json
344
print(api_json['when']) # 1701435000 (Unix timestamp)
345
```
346
347
### Complex Custom Type Example
348
349
```python
350
import jsons
351
from typing import Dict, Any
352
import base64
353
354
class SecureData:
355
"""Custom class that encrypts data during serialization"""
356
357
def __init__(self, data: str, key: str = "default"):
358
self.data = data
359
self.key = key
360
361
def encrypt(self) -> str:
362
# Simple base64 encoding for demo (use real encryption in production)
363
encoded = base64.b64encode(self.data.encode()).decode()
364
return f"{self.key}:{encoded}"
365
366
@classmethod
367
def decrypt(cls, encrypted: str) -> 'SecureData':
368
key, encoded = encrypted.split(':', 1)
369
data = base64.b64decode(encoded.encode()).decode()
370
return cls(data, key)
371
372
# Custom serializer for SecureData
373
def secure_data_serializer(obj: SecureData, **kwargs) -> str:
374
return obj.encrypt()
375
376
# Custom deserializer for SecureData
377
def secure_data_deserializer(json_str: str, cls, **kwargs) -> SecureData:
378
return SecureData.decrypt(json_str)
379
380
# Custom validator for SecureData
381
def secure_data_validator(obj: SecureData, **kwargs) -> bool:
382
if not obj.data:
383
raise jsons.ValidationError("SecureData cannot have empty data")
384
if len(obj.key) < 3:
385
raise jsons.ValidationError("SecureData key must be at least 3 characters")
386
return True
387
388
# Register all custom functions
389
jsons.set_serializer(secure_data_serializer, SecureData)
390
jsons.set_deserializer(secure_data_deserializer, SecureData)
391
jsons.set_validator(secure_data_validator, SecureData)
392
393
# Announce the class for better performance
394
jsons.announce_class(SecureData)
395
396
# Usage example
397
from dataclasses import dataclass
398
399
@dataclass
400
class UserProfile:
401
username: str
402
sensitive_info: SecureData
403
404
profile = UserProfile(
405
username="alice",
406
sensitive_info=SecureData("Social Security: 123-45-6789", "user_alice_key")
407
)
408
409
# Serialize (data gets encrypted)
410
serialized = jsons.dump(profile)
411
print(serialized)
412
# {
413
# 'username': 'alice',
414
# 'sensitive_info': 'user_alice_key:U29jaWFsIFNlY3VyaXR5OiAxMjMtNDUtNjc4OQ=='
415
# }
416
417
# Deserialize (data gets decrypted)
418
restored = jsons.load(serialized, UserProfile)
419
print(restored.sensitive_info.data) # 'Social Security: 123-45-6789'
420
print(restored.sensitive_info.key) # 'user_alice_key'
421
422
# Validation works automatically
423
try:
424
invalid_profile = UserProfile("bob", SecureData("", "x")) # Invalid data
425
jsons.validate(invalid_profile)
426
except jsons.ValidationError as e:
427
print(f"Validation failed: {e}")
428
```