0
# Type System
1
2
Comprehensive support for Python's type system including built-in types, collections, datetime objects, Enum types, Union types, and custom objects. The jsons library provides default serializers and deserializers for all common Python types with intelligent type inference.
3
4
## Capabilities
5
6
### Built-in Type Support
7
8
jsons automatically handles serialization and deserialization for these Python types:
9
10
#### Primitive Types
11
- **str** - String values (no conversion needed)
12
- **int** - Integer numbers
13
- **float** - Floating-point numbers
14
- **bool** - Boolean values (True/False)
15
- **None** - Null values
16
17
#### Numeric Types
18
- **complex** - Complex numbers (serialized as dict with 'real' and 'imag' keys)
19
- **Decimal** - High-precision decimal numbers (serialized as string)
20
21
#### Collection Types
22
- **list** - Lists and List[T] type hints
23
- **tuple** - Tuples and Tuple[T, ...] type hints
24
- **dict** - Dictionaries and Dict[K, V] type hints
25
- **set** - Sets (serialized as lists)
26
- **frozenset** - Frozen sets (serialized as lists)
27
- **DefaultDict** - Default dictionaries with default factory preservation
28
29
#### Date and Time Types
30
- **datetime** - Date and time objects (ISO format strings)
31
- **date** - Date objects (ISO date strings)
32
- **time** - Time objects (ISO time strings)
33
- **timezone** - Timezone objects
34
- **timedelta** - Time duration objects
35
- **ZoneInfo** - Timezone info objects (Python 3.9+)
36
37
#### Other Built-in Types
38
- **UUID** - Universally unique identifiers (serialized as strings)
39
- **Enum** - Enumeration values (serialized as enum values)
40
- **IntEnum** - Integer enumeration values
41
- **pathlib.Path** - Path objects (serialized as strings)
42
- **Mapping** - Abstract base class for mappings
43
- **Iterable** - Abstract base class for iterables
44
45
### Generic Type Support
46
47
```python { .api }
48
# Automatic handling of typing module generics:
49
# List[T], Tuple[T, ...], Dict[K, V], Set[T], FrozenSet[T]
50
# Union[T1, T2, ...], Optional[T], DefaultDict[K, V]
51
# Mapping[K, V], Iterable[T]
52
```
53
54
## Usage Examples
55
56
### Primitive and Numeric Types
57
58
```python
59
import jsons
60
from decimal import Decimal
61
62
# Primitive types work automatically
63
data = {
64
'name': 'John', # str
65
'age': 30, # int
66
'height': 5.9, # float
67
'is_active': True, # bool
68
'notes': None # NoneType
69
}
70
71
serialized = jsons.dump(data)
72
restored = jsons.load(serialized, dict)
73
print(restored == data) # True
74
75
# Complex numbers
76
complex_num = 3 + 4j
77
serialized = jsons.dump(complex_num)
78
print(serialized) # {'real': 3.0, 'imag': 4.0}
79
restored = jsons.load(serialized, complex)
80
print(restored) # (3+4j)
81
82
# Decimal precision
83
price = Decimal('19.99')
84
serialized = jsons.dump(price)
85
print(serialized) # '19.99'
86
restored = jsons.load(serialized, Decimal)
87
print(restored == price) # True
88
```
89
90
### Collection Types
91
92
```python
93
import jsons
94
from typing import List, Dict, Tuple, Set, Optional
95
from collections import defaultdict
96
97
# Lists with type hints
98
numbers: List[int] = [1, 2, 3, 4, 5]
99
serialized = jsons.dump(numbers)
100
restored = jsons.load(serialized, List[int])
101
print(restored) # [1, 2, 3, 4, 5]
102
103
# Dictionaries with type hints
104
scores: Dict[str, int] = {'Alice': 95, 'Bob': 87, 'Charlie': 92}
105
serialized = jsons.dump(scores)
106
restored = jsons.load(serialized, Dict[str, int])
107
print(restored) # {'Alice': 95, 'Bob': 87, 'Charlie': 92}
108
109
# Tuples with specific types
110
coordinates: Tuple[float, float, str] = (40.7128, -74.0060, 'NYC')
111
serialized = jsons.dump(coordinates)
112
restored = jsons.load(serialized, Tuple[float, float, str])
113
print(restored) # (40.7128, -74.006, 'NYC')
114
115
# Sets (serialized as lists)
116
unique_tags: Set[str] = {'python', 'json', 'serialization'}
117
serialized = jsons.dump(unique_tags)
118
print(serialized) # ['python', 'json', 'serialization'] (order may vary)
119
restored = jsons.load(serialized, Set[str])
120
print(type(restored)) # <class 'set'>
121
122
# DefaultDict with factory function preservation
123
def_dict = defaultdict(list)
124
def_dict['fruits'].extend(['apple', 'banana'])
125
def_dict['vegetables'].extend(['carrot', 'broccoli'])
126
127
serialized = jsons.dump(def_dict)
128
restored = jsons.load(serialized, defaultdict)
129
print(restored['new_key']) # [] (default factory still works)
130
```
131
132
### Date and Time Types
133
134
```python
135
import jsons
136
from datetime import datetime, date, time, timezone, timedelta
137
from zoneinfo import ZoneInfo # Python 3.9+
138
139
# Datetime objects
140
now = datetime.now()
141
serialized = jsons.dump(now)
142
print(serialized) # '2023-12-01T10:30:00.123456'
143
restored = jsons.load(serialized, datetime)
144
print(type(restored)) # <class 'datetime.datetime'>
145
146
# Date objects
147
today = date.today()
148
serialized = jsons.dump(today)
149
print(serialized) # '2023-12-01'
150
restored = jsons.load(serialized, date)
151
152
# Time objects
153
current_time = time(14, 30, 45)
154
serialized = jsons.dump(current_time)
155
print(serialized) # '14:30:45'
156
restored = jsons.load(serialized, time)
157
158
# Timezone-aware datetime
159
utc_time = datetime.now(timezone.utc)
160
serialized = jsons.dump(utc_time)
161
restored = jsons.load(serialized, datetime)
162
print(restored.tzinfo) # timezone.utc
163
164
# Timedelta objects
165
duration = timedelta(days=7, hours=3, minutes=30)
166
serialized = jsons.dump(duration)
167
restored = jsons.load(serialized, timedelta)
168
print(restored.days) # 7
169
170
# ZoneInfo (Python 3.9+)
171
ny_tz = ZoneInfo('America/New_York')
172
ny_time = datetime.now(ny_tz)
173
serialized = jsons.dump(ny_time)
174
restored = jsons.load(serialized, datetime)
175
```
176
177
### Enum and Special Types
178
179
```python
180
import jsons
181
from enum import Enum, IntEnum
182
from uuid import uuid4, UUID
183
from pathlib import Path
184
185
# Enum types
186
class Status(Enum):
187
PENDING = 'pending'
188
APPROVED = 'approved'
189
REJECTED = 'rejected'
190
191
class Priority(IntEnum):
192
LOW = 1
193
MEDIUM = 2
194
HIGH = 3
195
196
status = Status.APPROVED
197
priority = Priority.HIGH
198
199
status_serialized = jsons.dump(status)
200
priority_serialized = jsons.dump(priority)
201
202
print(status_serialized) # 'approved'
203
print(priority_serialized) # 2
204
205
status_restored = jsons.load(status_serialized, Status)
206
priority_restored = jsons.load(priority_serialized, Priority)
207
208
print(status_restored == Status.APPROVED) # True
209
print(priority_restored == Priority.HIGH) # True
210
211
# UUID objects
212
unique_id = uuid4()
213
serialized = jsons.dump(unique_id)
214
print(serialized) # '12345678-1234-5678-1234-567812345678'
215
restored = jsons.load(serialized, UUID)
216
print(type(restored)) # <class 'uuid.UUID'>
217
218
# Path objects
219
file_path = Path('/home/user/documents/file.txt')
220
serialized = jsons.dump(file_path)
221
print(serialized) # '/home/user/documents/file.txt'
222
restored = jsons.load(serialized, Path)
223
print(type(restored)) # <class 'pathlib.PosixPath'>
224
```
225
226
### Union and Optional Types
227
228
```python
229
import jsons
230
from typing import Union, Optional, List
231
from dataclasses import dataclass
232
233
@dataclass
234
class NumberContainer:
235
value: Union[int, float, str] # Can be int, float, or str
236
optional_notes: Optional[str] = None # Can be str or None
237
238
# Union types - jsons automatically detects the correct type
239
containers = [
240
NumberContainer(42), # int
241
NumberContainer(3.14), # float
242
NumberContainer("not_a_number"), # str
243
NumberContainer(100, "Important note") # with optional field
244
]
245
246
serialized_list = jsons.dump(containers)
247
print(serialized_list)
248
# [
249
# {'value': 42, 'optional_notes': None},
250
# {'value': 3.14, 'optional_notes': None},
251
# {'value': 'not_a_number', 'optional_notes': None},
252
# {'value': 100, 'optional_notes': 'Important note'}
253
# ]
254
255
restored_list = jsons.load(serialized_list, List[NumberContainer])
256
print(type(restored_list[0].value)) # <class 'int'>
257
print(type(restored_list[1].value)) # <class 'float'>
258
print(type(restored_list[2].value)) # <class 'str'>
259
260
# Optional types work seamlessly
261
@dataclass
262
class User:
263
username: str
264
email: Optional[str] = None
265
266
user1 = User("alice") # email is None
267
user2 = User("bob", "bob@example.com") # email provided
268
269
users = [user1, user2]
270
serialized = jsons.dump(users)
271
restored = jsons.load(serialized, List[User])
272
273
print(restored[0].email) # None
274
print(restored[1].email) # 'bob@example.com'
275
```
276
277
### Custom Object Support
278
279
```python
280
import jsons
281
from dataclasses import dataclass
282
from typing import List
283
284
@dataclass
285
class Address:
286
street: str
287
city: str
288
zip_code: str
289
290
@dataclass
291
class Person:
292
name: str
293
age: int
294
address: Address
295
phone_numbers: List[str]
296
297
# Nested custom objects work automatically with type hints
298
address = Address("123 Main St", "Anytown", "12345")
299
person = Person("John Doe", 30, address, ["+1-555-0100", "+1-555-0101"])
300
301
# Automatic serialization of nested objects
302
serialized = jsons.dump(person)
303
print(serialized)
304
# {
305
# 'name': 'John Doe',
306
# 'age': 30,
307
# 'address': {'street': '123 Main St', 'city': 'Anytown', 'zip_code': '12345'},
308
# 'phone_numbers': ['+1-555-0100', '+1-555-0101']
309
# }
310
311
# Automatic deserialization with proper type reconstruction
312
restored = jsons.load(serialized, Person)
313
print(type(restored.address)) # <class '__main__.Address'>
314
print(restored.address.city) # 'Anytown'
315
print(len(restored.phone_numbers)) # 2
316
```
317
318
### Generic Collections with Custom Objects
319
320
```python
321
import jsons
322
from dataclasses import dataclass
323
from typing import Dict, List, Optional
324
from datetime import datetime
325
326
@dataclass
327
class Task:
328
id: int
329
title: str
330
completed: bool
331
due_date: Optional[datetime] = None
332
333
@dataclass
334
class Project:
335
name: str
336
tasks: Dict[str, List[Task]] # Complex nested generic structure
337
338
# Complex nested structures with multiple generics
339
project = Project(
340
name="Website Redesign",
341
tasks={
342
"frontend": [
343
Task(1, "Create mockups", False, datetime(2023, 12, 15)),
344
Task(2, "Implement UI", False, datetime(2023, 12, 20))
345
],
346
"backend": [
347
Task(3, "API design", True),
348
Task(4, "Database schema", False, datetime(2023, 12, 10))
349
]
350
}
351
)
352
353
# Serialize complex nested structure
354
serialized = jsons.dump(project)
355
print(serialized['tasks']['frontend'][0]['title']) # 'Create mockups'
356
357
# Deserialize with full type reconstruction
358
restored = jsons.load(serialized, Project)
359
print(type(restored.tasks)) # <class 'dict'>
360
print(type(restored.tasks['frontend'])) # <class 'list'>
361
print(type(restored.tasks['frontend'][0])) # <class '__main__.Task'>
362
print(type(restored.tasks['frontend'][0].due_date)) # <class 'datetime.datetime'>
363
```