0
# Type Checking
1
2
Customizable type checking system for JSON Schema types with built-in type checkers for each draft version and extensibility for custom type definitions. The type system maps JSON Schema types to Python types and validation logic.
3
4
## Capabilities
5
6
### TypeChecker
7
8
The main class for JSON Schema type validation, supporting both built-in and custom type definitions.
9
10
```python { .api }
11
class TypeChecker:
12
"""
13
A type property checker for JSON Schema type keyword validation.
14
15
Converts between JSON Schema types and Python types/objects.
16
Supports customization and extension of type checking behavior.
17
"""
18
19
def __init__(self, type_checkers=()):
20
"""
21
Initialize type checker.
22
23
Parameters:
24
- type_checkers: Mapping of type names to checker functions
25
"""
26
27
def is_type(self, instance, type):
28
"""
29
Check if instance is of the specified JSON Schema type.
30
31
Parameters:
32
- instance: Value to check
33
- type: JSON Schema type name
34
35
Returns:
36
- bool: True if instance is of the specified type
37
38
Raises:
39
- UndefinedTypeCheck: If type is not defined
40
"""
41
42
def redefine(self, type, fn):
43
"""
44
Create new TypeChecker with redefined type.
45
46
Parameters:
47
- type: Type name to redefine
48
- fn: Type checking function
49
50
Returns:
51
- TypeChecker: New TypeChecker instance
52
"""
53
54
def redefine_many(self, definitions=()):
55
"""
56
Create new TypeChecker with multiple redefined types.
57
58
Parameters:
59
- definitions: Dictionary mapping type names to checker functions
60
61
Returns:
62
- TypeChecker: New TypeChecker instance
63
"""
64
65
def remove(self, *types):
66
"""
67
Create new TypeChecker without specified types.
68
69
Parameters:
70
- types: Type names to remove
71
72
Returns:
73
- TypeChecker: New TypeChecker instance
74
75
Raises:
76
- UndefinedTypeCheck: If any type is not defined
77
"""
78
```
79
80
### Built-in Type Checkers
81
82
Pre-configured type checkers for each JSON Schema draft version.
83
84
```python { .api }
85
# Type checkers for each draft
86
draft202012_type_checker: TypeChecker
87
draft201909_type_checker: TypeChecker
88
draft7_type_checker: TypeChecker
89
draft6_type_checker: TypeChecker
90
draft4_type_checker: TypeChecker
91
draft3_type_checker: TypeChecker
92
```
93
94
### Built-in Type Checking Functions
95
96
Individual type checking functions for standard JSON Schema types.
97
98
```python { .api }
99
def is_array(checker, instance):
100
"""
101
Check if instance is an array (Python list).
102
103
Parameters:
104
- checker: TypeChecker instance
105
- instance: Value to check
106
107
Returns:
108
- bool: True if instance is a list
109
"""
110
111
def is_bool(checker, instance):
112
"""
113
Check if instance is a boolean.
114
115
Parameters:
116
- checker: TypeChecker instance
117
- instance: Value to check
118
119
Returns:
120
- bool: True if instance is a boolean
121
"""
122
123
def is_integer(checker, instance):
124
"""
125
Check if instance is an integer.
126
127
Parameters:
128
- checker: TypeChecker instance
129
- instance: Value to check
130
131
Returns:
132
- bool: True if instance is an integer (excludes booleans)
133
"""
134
135
def is_null(checker, instance):
136
"""
137
Check if instance is null (Python None).
138
139
Parameters:
140
- checker: TypeChecker instance
141
- instance: Value to check
142
143
Returns:
144
- bool: True if instance is None
145
"""
146
147
def is_number(checker, instance):
148
"""
149
Check if instance is a number.
150
151
Parameters:
152
- checker: TypeChecker instance
153
- instance: Value to check
154
155
Returns:
156
- bool: True if instance is numeric (excludes booleans)
157
"""
158
159
def is_object(checker, instance):
160
"""
161
Check if instance is an object (Python dict).
162
163
Parameters:
164
- checker: TypeChecker instance
165
- instance: Value to check
166
167
Returns:
168
- bool: True if instance is a dictionary
169
"""
170
171
def is_string(checker, instance):
172
"""
173
Check if instance is a string.
174
175
Parameters:
176
- checker: TypeChecker instance
177
- instance: Value to check
178
179
Returns:
180
- bool: True if instance is a string
181
"""
182
183
def is_any(checker, instance):
184
"""
185
Check if instance is any type (always returns True).
186
187
Used for Draft 3 "any" type.
188
189
Parameters:
190
- checker: TypeChecker instance
191
- instance: Value to check
192
193
Returns:
194
- bool: Always True
195
"""
196
```
197
198
## Usage Examples
199
200
### Basic Type Checking
201
202
```python
203
from jsonschema import Draft202012Validator
204
from jsonschema._types import draft202012_type_checker
205
206
# Direct type checking
207
checker = draft202012_type_checker
208
209
print(checker.is_type("hello", "string")) # True
210
print(checker.is_type(123, "integer")) # True
211
print(checker.is_type(123.5, "number")) # True
212
print(checker.is_type([1, 2, 3], "array")) # True
213
print(checker.is_type({"a": 1}, "object")) # True
214
print(checker.is_type(None, "null")) # True
215
print(checker.is_type(True, "boolean")) # True
216
217
# Type checking in validation
218
schema = {"type": "integer"}
219
validator = Draft202012Validator(schema)
220
221
validator.validate(42) # Valid
222
validator.validate(42.0) # Valid (Draft 6+ allows integers as floats)
223
validator.validate("42") # Invalid - string, not integer
224
```
225
226
### Custom Type Definitions
227
228
```python
229
from jsonschema import Draft202012Validator, create
230
from jsonschema._types import TypeChecker, draft202012_type_checker
231
import decimal
232
233
# Define custom type checker function
234
def is_decimal(checker, instance):
235
"""Check if instance is a decimal number."""
236
return isinstance(instance, decimal.Decimal)
237
238
def is_positive_number(checker, instance):
239
"""Check if instance is a positive number."""
240
return (isinstance(instance, (int, float)) and
241
not isinstance(instance, bool) and
242
instance > 0)
243
244
# Create custom type checker
245
custom_type_checker = draft202012_type_checker.redefine_many({
246
"decimal": is_decimal,
247
"positive-number": is_positive_number
248
})
249
250
# Create validator with custom types
251
validator_class = create(
252
meta_schema=Draft202012Validator.META_SCHEMA,
253
validators=Draft202012Validator.VALIDATORS,
254
type_checker=custom_type_checker
255
)
256
257
# Use custom types in schema
258
schema = {
259
"type": "object",
260
"properties": {
261
"price": {"type": "decimal"},
262
"quantity": {"type": "positive-number"}
263
}
264
}
265
266
validator = validator_class(schema)
267
268
# Test custom types
269
import decimal
270
test_data = {
271
"price": decimal.Decimal("19.99"),
272
"quantity": 5
273
}
274
275
validator.validate(test_data) # Should pass
276
277
# Invalid data
278
invalid_data = {
279
"price": 19.99, # float, not Decimal
280
"quantity": -1 # not positive
281
}
282
283
errors = list(validator.iter_errors(invalid_data))
284
for error in errors:
285
print(f"Type error: {error.message}")
286
```
287
288
### Extending Existing Type Checkers
289
290
```python
291
from jsonschema._types import draft202012_type_checker
292
from collections import namedtuple
293
294
# Define custom object type that includes namedtuples
295
def is_object_or_namedtuple(checker, instance):
296
"""Check if instance is dict or namedtuple."""
297
return (isinstance(instance, dict) or
298
(hasattr(instance, '_fields') and
299
hasattr(instance, '_asdict')))
300
301
# Extend type checker
302
extended_checker = draft202012_type_checker.redefine(
303
"object", is_object_or_namedtuple
304
)
305
306
# Test with namedtuple
307
Person = namedtuple('Person', ['name', 'age'])
308
person = Person('Alice', 30)
309
310
print(extended_checker.is_type(person, "object")) # True
311
print(draft202012_type_checker.is_type(person, "object")) # False
312
```
313
314
### Removing Type Support
315
316
```python
317
from jsonschema._types import draft202012_type_checker
318
319
# Create type checker without null support
320
no_null_checker = draft202012_type_checker.remove("null")
321
322
try:
323
no_null_checker.is_type(None, "null")
324
except UndefinedTypeCheck as e:
325
print(f"Type undefined: {e}")
326
327
# Available types
328
print("Available types:", list(no_null_checker._type_checkers.keys()))
329
```
330
331
### Draft-Specific Type Differences
332
333
```python
334
from jsonschema._types import (
335
draft3_type_checker,
336
draft4_type_checker,
337
draft6_type_checker
338
)
339
340
# Draft 3 supports "any" type
341
print("Draft 3 types:", list(draft3_type_checker._type_checkers.keys()))
342
# Includes: 'any', 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string'
343
344
# Draft 4+ removes "any" type
345
print("Draft 4 types:", list(draft4_type_checker._type_checkers.keys()))
346
# Excludes 'any'
347
348
# Draft 6+ changes integer handling
349
print(draft4_type_checker.is_type(42.0, "integer")) # False
350
print(draft6_type_checker.is_type(42.0, "integer")) # True (if 42.0.is_integer())
351
```
352
353
### Type Checking in Custom Validators
354
355
```python
356
from jsonschema import Draft202012Validator
357
from jsonschema._types import TypeChecker
358
359
# Create validator with custom type checking
360
def is_non_empty_string(checker, instance):
361
"""Check if instance is a non-empty string."""
362
return isinstance(instance, str) and len(instance) > 0
363
364
custom_checker = TypeChecker().redefine_many({
365
"string": is_string,
366
"non-empty-string": is_non_empty_string,
367
"array": is_array,
368
"object": is_object,
369
"number": is_number,
370
"integer": is_integer,
371
"boolean": is_bool,
372
"null": is_null
373
})
374
375
# Import required items
376
from jsonschema._types import is_string, is_array, is_object, is_number, is_integer, is_bool, is_null
377
378
# Use in validator
379
validator = Draft202012Validator(
380
{"type": "non-empty-string"},
381
type_checker=custom_checker
382
)
383
384
validator.validate("hello") # Valid
385
try:
386
validator.validate("") # Invalid - empty string
387
except ValidationError as e:
388
print(f"Type error: {e.message}")
389
```
390
391
### Type Checking Function Signature
392
393
All type checking functions must follow this signature:
394
395
```python
396
def my_type_checker(checker, instance):
397
"""
398
Custom type checking function.
399
400
Parameters:
401
- checker: The TypeChecker instance calling this function
402
- instance: The value to type-check
403
404
Returns:
405
- bool: True if instance is of this type, False otherwise
406
"""
407
# Custom type checking logic here
408
return isinstance(instance, MyCustomType)
409
```