0
# JSON Serialization Interfaces
1
2
Core interfaces and base classes providing JSON serialization and deserialization functionality for all JOSE objects. These interfaces define the contract for converting Python objects to/from JSON representations with proper error handling and type safety.
3
4
## Capabilities
5
6
### JSON Serialization Interface
7
8
Base interface implemented by all JOSE objects for JSON serialization and deserialization with support for partial and full serialization modes.
9
10
```python { .api }
11
class JSONDeSerializable:
12
"""Interface for JSON serializable/deserializable objects"""
13
14
def to_partial_json(self) -> Any:
15
"""
16
Perform partial serialization to JSON-compatible objects.
17
18
Partial serialization may include other JSONDeSerializable objects
19
that need further processing for full serialization.
20
21
Returns:
22
Any: Partially serialized object (may contain JSONDeSerializable instances)
23
24
Raises:
25
josepy.errors.SerializationError: If serialization fails
26
"""
27
28
def to_json(self) -> Any:
29
"""
30
Perform full serialization to basic Python types only.
31
32
Recursively serializes all JSONDeSerializable objects to basic types
33
suitable for JSON encoding (dict, list, str, int, float, bool, None).
34
35
Returns:
36
Any: Fully serialized object containing only basic Python types
37
38
Raises:
39
josepy.errors.SerializationError: If serialization fails
40
"""
41
42
@classmethod
43
def from_json(cls, jobj: Any) -> 'JSONDeSerializable':
44
"""
45
Deserialize from JSON-compatible Python object.
46
47
Parameters:
48
- jobj: Python object from json.loads() or equivalent
49
50
Returns:
51
JSONDeSerializable: Deserialized object instance
52
53
Raises:
54
josepy.errors.DeserializationError: If deserialization fails
55
"""
56
57
@classmethod
58
def json_loads(cls, json_string: Union[str, bytes]) -> 'JSONDeSerializable':
59
"""
60
Deserialize from JSON document string.
61
62
Parameters:
63
- json_string: JSON document as string or bytes
64
65
Returns:
66
JSONDeSerializable: Deserialized object instance
67
68
Raises:
69
josepy.errors.DeserializationError: If JSON parsing or deserialization fails
70
"""
71
72
def json_dumps(self, **kwargs) -> str:
73
"""
74
Serialize to JSON document string.
75
76
Parameters:
77
- **kwargs: Additional arguments passed to json.dumps()
78
79
Returns:
80
str: JSON document string
81
"""
82
83
def json_dumps_pretty(self) -> str:
84
"""
85
Serialize to pretty-formatted JSON string.
86
87
Returns:
88
str: Pretty-formatted JSON document with indentation and sorting
89
"""
90
91
@classmethod
92
def json_dump_default(cls, python_object: 'JSONDeSerializable') -> Any:
93
"""
94
Default serializer function for json.dumps().
95
96
This method is used as the 'default' parameter for json.dumps()
97
to handle JSONDeSerializable objects.
98
99
Parameters:
100
- python_object: Object to serialize
101
102
Returns:
103
Any: Serialized representation
104
105
Raises:
106
TypeError: If object is not JSONDeSerializable
107
"""
108
```
109
110
## Usage Examples
111
112
### Basic Serialization
113
114
```python
115
from josepy import JWK, JWKRSA
116
from cryptography.hazmat.primitives.asymmetric import rsa
117
from cryptography.hazmat.backends import default_backend
118
119
# Create a JWK (which implements JSONDeSerializable)
120
private_key = rsa.generate_private_key(65537, 2048, default_backend())
121
jwk = JWKRSA(key=private_key)
122
123
# Serialize to JSON string
124
jwk_json = jwk.json_dumps()
125
print(f"JWK JSON: {jwk_json}")
126
127
# Pretty print
128
pretty_json = jwk.json_dumps_pretty()
129
print(f"Pretty JWK:\n{pretty_json}")
130
131
# Deserialize from JSON
132
loaded_jwk = JWKRSA.json_loads(jwk_json)
133
assert jwk.thumbprint() == loaded_jwk.thumbprint()
134
```
135
136
### Custom JSON Encoding
137
138
```python
139
import json
140
from josepy import JWS, Header, RS256
141
142
# Create JWS (JSONDeSerializable object)
143
payload = b'{"user": "alice", "role": "admin"}'
144
jws = JWS.sign(payload, key=jwk, alg=RS256)
145
146
# Use custom JSON encoder
147
custom_json = json.dumps(jws, default=JWS.json_dump_default, indent=2)
148
print(f"Custom encoded JWS:\n{custom_json}")
149
150
# Load back
151
loaded_jws = JWS.from_json(json.loads(custom_json))
152
```
153
154
### Partial vs Full Serialization
155
156
```python
157
# Demonstrate the difference between partial and full serialization
158
header = Header(alg=RS256, jwk=jwk.public_key())
159
160
# Partial serialization may contain JSONDeSerializable objects
161
partial = header.to_partial_json()
162
print(f"Partial serialization type of 'jwk': {type(partial.get('jwk'))}")
163
# Output: <class 'josepy.jwk.JWKRSA'>
164
165
# Full serialization contains only basic Python types
166
full = header.to_json()
167
print(f"Full serialization type of 'jwk': {type(full.get('jwk'))}")
168
# Output: <class 'dict'>
169
```
170
171
### Error Handling
172
173
```python
174
from josepy.errors import DeserializationError, SerializationError
175
176
try:
177
# Invalid JSON structure
178
invalid_jwk = JWKRSA.json_loads('{"invalid": "structure"}')
179
except DeserializationError as e:
180
print(f"Deserialization failed: {e}")
181
182
try:
183
# Malformed JSON string
184
malformed_jwk = JWKRSA.json_loads('invalid json')
185
except DeserializationError as e:
186
print(f"JSON parsing failed: {e}")
187
```
188
189
### Integration with Standard JSON Module
190
191
```python
192
import json
193
from josepy import JWK, JWKRSA, Header, RS256
194
195
# List of JOSE objects
196
jose_objects = [
197
JWKRSA(key=private_key),
198
Header(alg=RS256, kid="key-1"),
199
# ... other JOSE objects
200
]
201
202
# Serialize list of JOSE objects
203
serialized = json.dumps(
204
jose_objects,
205
default=JSONDeSerializable.json_dump_default,
206
indent=2
207
)
208
209
print(f"Serialized JOSE objects:\n{serialized}")
210
211
# The serialized JSON can be loaded back using appropriate from_json methods
212
data = json.loads(serialized)
213
# Then reconstruct objects based on their types
214
```
215
216
## Design Principles
217
218
### Serialization Modes
219
220
1. **Partial Serialization** (`to_partial_json()`):
221
- May contain other `JSONDeSerializable` objects
222
- Suitable for intermediate processing
223
- Used internally by the serialization framework
224
225
2. **Full Serialization** (`to_json()`):
226
- Contains only basic Python types
227
- Suitable for final JSON encoding
228
- Recursively processes all nested objects
229
230
### Error Handling
231
232
- **SerializationError**: Raised when objects cannot be converted to JSON-compatible format
233
- **DeserializationError**: Raised when JSON data cannot be converted back to Python objects
234
- **TypeError**: Raised by `json_dump_default()` for non-JSONDeSerializable objects
235
236
### Integration Pattern
237
238
All JOSE objects (JWK, JWS, Header, etc.) implement this interface, providing:
239
- Consistent serialization behavior across all objects
240
- Automatic handling of nested JOSE objects
241
- Proper error reporting and debugging information
242
- Integration with standard Python `json` module