Flask provides built-in support for JSON serialization and deserialization with customizable behavior.
JSON Serialization
jsonify
jsonify(*args: Any, **kwargs: Any) -> Response
Serialize the given arguments as JSON and return a Response object with the application/json mimetype.
This requires an active app context and calls app.json.response().
A single value to serialize, or multiple values to treat as a list to serialize.
Treat as a dict to serialize.
Either positional or keyword arguments can be given, not both. If no arguments are given, None is serialized.
Returns: A Response object with JSON content and application/json mimetype.
Example
from flask import jsonify
@app.route('/api/user/<int:id>')
def get_user(id):
user = User.query.get_or_404(id)
return jsonify(
id=user.id,
username=user.username,
email=user.email
)
# Response: {"id": 1, "username": "john", "email": "[email protected]"}
@app.route('/api/users')
def get_users():
users = User.query.all()
return jsonify([u.to_dict() for u in users])
# Response: [{...}, {...}, ...]
@app.route('/api/status')
def status():
return jsonify({'status': 'ok', 'version': '1.0.0'})
# Multiple values as list
@app.route('/api/items')
def items():
return jsonify('item1', 'item2', 'item3')
# Response: ["item1", "item2", "item3"]
In debug mode, the output is formatted with indentation to make it easier to read.
dumps
dumps(obj: Any, **kwargs: Any) -> str
Serialize data as JSON to a string.
If current_app is available, it will use its app.json.dumps() method, otherwise it will use json.dumps.
Arguments passed to the dumps implementation.
Returns: JSON string representation of the object.
Example
from flask import json
data = {'name': 'John', 'age': 30, 'active': True}
json_string = json.dumps(data)
# Result: '{"name":"John","age":30,"active":true}'
# With custom options
json_string = json.dumps(data, indent=2, sort_keys=True)
# Result:
# {
# "active": true,
# "age": 30,
# "name": "John"
# }
# Serialize Decimal
from decimal import Decimal
data = {'price': Decimal('19.99')}
json_string = json.dumps(data)
# Result: '{"price":"19.99"}'
dump
dump(obj: Any, fp: IO[str], **kwargs: Any) -> None
Serialize data as JSON and write to a file.
If current_app is available, it will use its app.json.dump() method, otherwise it will use json.dump.
A file opened for writing text. Should use the UTF-8 encoding to be valid JSON.
Arguments passed to the dump implementation.
Example
from flask import json
data = {
'users': [
{'id': 1, 'name': 'John'},
{'id': 2, 'name': 'Jane'}
]
}
with open('data.json', 'w') as f:
json.dump(data, f, indent=2)
# With custom serialization
with open('config.json', 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
JSON Deserialization
loads
loads(s: str | bytes, **kwargs: Any) -> Any
Deserialize data as JSON from a string or bytes.
If current_app is available, it will use its app.json.loads() method, otherwise it will use json.loads.
Text or UTF-8 bytes to deserialize.
Arguments passed to the loads implementation.
Returns: The deserialized Python object.
Example
from flask import json
json_string = '{"name":"John","age":30}'
data = json.loads(json_string)
# Result: {'name': 'John', 'age': 30}
# From bytes
json_bytes = b'{"active":true}'
data = json.loads(json_bytes)
# Result: {'active': True}
# From request data
@app.route('/api/data', methods=['POST'])
def receive_data():
data = json.loads(request.data)
process_data(data)
return jsonify({'status': 'success'})
load
load(fp: IO[AnyStr], **kwargs: Any) -> Any
Deserialize data as JSON read from a file.
If current_app is available, it will use its app.json.load() method, otherwise it will use json.load.
A file opened for reading text or UTF-8 bytes.
Arguments passed to the load implementation.
Returns: The deserialized Python object.
Example
from flask import json
# Load from file
with open('data.json', 'r') as f:
data = json.load(f)
# Load configuration
with open('config.json', 'r', encoding='utf-8') as f:
config = json.load(f)
app.config.update(config)
# Load from uploaded file
@app.route('/upload', methods=['POST'])
def upload():
file = request.files['data']
data = json.load(file.stream)
return jsonify({'received': len(data)})
Custom JSON Provider
Flask allows you to customize JSON serialization behavior by overriding the default JSON provider.
Default JSON Provider
from flask.json.provider import DefaultJSONProvider
class CustomJSONProvider(DefaultJSONProvider):
def dumps(self, obj, **kwargs):
# Custom serialization logic
return super().dumps(obj, **kwargs)
def loads(self, s, **kwargs):
# Custom deserialization logic
return super().loads(s, **kwargs)
app.json = CustomJSONProvider(app)
Example: Custom Serialization
from flask.json.provider import DefaultJSONProvider
from datetime import datetime
from decimal import Decimal
import dataclasses
class CustomJSONProvider(DefaultJSONProvider):
def default(self, obj):
# Handle datetime objects
if isinstance(obj, datetime):
return obj.isoformat()
# Handle Decimal objects
if isinstance(obj, Decimal):
return float(obj)
# Handle dataclasses
if dataclasses.is_dataclass(obj):
return dataclasses.asdict(obj)
# Fallback to default behavior
return super().default(obj)
app.json = CustomJSONProvider(app)
# Now you can serialize these types
@app.route('/api/order/<int:id>')
def get_order(id):
order = Order.query.get_or_404(id)
return jsonify(
id=order.id,
created_at=order.created_at, # datetime object
total=order.total, # Decimal object
items=[item for item in order.items] # dataclass objects
)
Example: Compact JSON
from flask.json.provider import DefaultJSONProvider
class CompactJSONProvider(DefaultJSONProvider):
def dumps(self, obj, **kwargs):
# Remove all whitespace for smaller response size
kwargs.setdefault('separators', (',', ':'))
return super().dumps(obj, **kwargs)
app.json = CompactJSONProvider(app)
Example: Pretty JSON in Debug
from flask.json.provider import DefaultJSONProvider
class PrettyJSONProvider(DefaultJSONProvider):
def dumps(self, obj, **kwargs):
if self.app.debug:
kwargs.setdefault('indent', 2)
kwargs.setdefault('sort_keys', True)
return super().dumps(obj, **kwargs)
app.json = PrettyJSONProvider(app)
Request JSON Data
Access JSON data from incoming requests:
@app.route('/api/create', methods=['POST'])
def create():
# Get JSON data from request body
data = request.get_json()
# With error handling
if not request.is_json:
return jsonify({'error': 'Content-Type must be application/json'}), 400
data = request.get_json()
if data is None:
return jsonify({'error': 'Invalid JSON'}), 400
# Access specific fields
name = data.get('name')
email = data.get('email')
# Create resource
user = User(name=name, email=email)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201
# Force JSON parsing even if Content-Type is wrong
@app.route('/api/data', methods=['POST'])
def receive_data():
data = request.get_json(force=True)
return jsonify({'received': data})
Error Handling
Return JSON error responses:
from flask import jsonify
@app.errorhandler(404)
def not_found(error):
return jsonify({
'error': 'Not Found',
'message': str(error)
}), 404
@app.errorhandler(400)
def bad_request(error):
return jsonify({
'error': 'Bad Request',
'message': str(error)
}), 400
@app.errorhandler(500)
def internal_error(error):
return jsonify({
'error': 'Internal Server Error',
'message': 'An unexpected error occurred'
}), 500
# Custom error response
@app.route('/api/user/<int:id>')
def get_user(id):
user = User.query.get(id)
if user is None:
return jsonify({
'error': 'User not found',
'user_id': id
}), 404
return jsonify(user.to_dict())
Best Practices
Always validate JSON input before processing it. Never trust user input.
Recommendations:
- Use jsonify for responses - It automatically sets the correct content type
- Validate input - Check required fields and data types
- Handle errors gracefully - Return meaningful error messages
- Use custom providers - Customize serialization for your needs
- Set proper status codes - Use appropriate HTTP status codes with JSON responses
- Consider performance - Use compact JSON for production, pretty JSON for development
from flask import jsonify, request
from marshmallow import Schema, fields, ValidationError
class UserSchema(Schema):
name = fields.Str(required=True)
email = fields.Email(required=True)
age = fields.Int()
@app.route('/api/users', methods=['POST'])
def create_user():
# Validate content type
if not request.is_json:
return jsonify({'error': 'Content-Type must be application/json'}), 415
# Parse JSON
try:
data = request.get_json()
except Exception as e:
return jsonify({'error': 'Invalid JSON', 'message': str(e)}), 400
# Validate schema
schema = UserSchema()
try:
validated_data = schema.load(data)
except ValidationError as e:
return jsonify({'error': 'Validation failed', 'details': e.messages}), 400
# Create user
user = User(**validated_data)
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201