or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

class-integration.mdconfiguration.mdcore-serialization.mdcustomization.mdindex.mdkey-transformation.mdtype-system.md

customization.mddocs/

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

```