用 Python 标准库校验 JSON 配置:类型、默认值与错误位置
把配置保存为 JSON 后,json.load() 只负责解析语法,不会保证字段符合应用的要求。例如端口可能被写成 true,功能列表里可能混入数字。程序如果直接使用这些值,错误常常会在更远的位置才出现。下面用 Python 标准库在读取入口集中检查配置,并给出能定位问题的提示。
目标与规则
假设配置有三个可选字段:port 是 1~65535 的整数,默认 8080;debug 是布尔值,默认 false;features 是字符串数组,默认空数组。示例对未知字段暂时忽略,返回值只包含这三个字段。
可运行脚本
保存为 validate_json_config.py,使用 Python 3.8 或更新版本运行:
import json
from pathlib import Path
class ConfigError(ValueError):
pass
def load_config(path: Path) -> dict:
try:
with path.open("r", encoding="utf-8") as handle:
raw = json.load(handle)
except FileNotFoundError as error:
raise ConfigError(f"找不到配置文件:{path}") from error
except json.JSONDecodeError as error:
raise ConfigError(
f"JSON 格式错误:第 {error.lineno} 行,第 {error.colno} 列:{error.msg}"
) from error
if not isinstance(raw, dict):
raise ConfigError("配置根节点必须是对象")
port = raw.get("port", 8080)
if type(port) is not int or not 1 <= port <= 65535:
raise ConfigError("port 必须是 1 到 65535 之间的整数")
debug = raw.get("debug", False)
if type(debug) is not bool:
raise ConfigError("debug 必须是布尔值")
features = raw.get("features", [])
if not isinstance(features, list) or any(type(item) is not str for item in features):
raise ConfigError("features 必须是字符串数组")
return {"port": port, "debug": debug, "features": features}
if __name__ == "__main__":
import tempfile
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "config.json"
samples = [
('{"port": 9000, "debug": true, "features": ["搜索"]}', "有效配置"),
('{"port": true}', "布尔值不能当端口"),
('{"port": 70000}', "越界端口"),
('{"features": ["搜索", 1]}', "错误的数组元素"),
('{"port": 9000,}', "JSON 语法错误"),
]
for source, label in samples:
path.write_text(source, encoding="utf-8")
try:
print(f"{label}:{load_config(path)}")
except ConfigError as error:
print(f"{label}:{error}")
我在本地运行后得到:
有效配置:{'port': 9000, 'debug': True, 'features': ['搜索']}
布尔值不能当端口:port 必须是 1 到 65535 之间的整数
越界端口:port 必须是 1 到 65535 之间的整数
错误的数组元素:features 必须是字符串数组
JSON 语法错误:JSON 格式错误:第 1 行,第 15 列:Expecting property name enclosed in double quotes
几个容易忽略的细节
布尔值与整数。 Python 中 isinstance(True, int) 为真,因为 bool 是 int 的子类。端口字段不应接受 true,所以这里使用 type(port) is int。它也会拒绝整数子类;对来自 JSON 的值来说,解析器只会产生内置的整数类型,这个约束是合适的。
缺省值与错误值。 dict.get() 只在字段缺失时使用默认值。{"port": null} 并不会变成 8080,而是会被类型检查拒绝。这能避免把填写错误悄悄掩盖成默认配置。空对象 {} 则会得到 {'port': 8080, 'debug': False, 'features': []}。
语法错误的位置。 JSONDecodeError 提供 lineno 和 colno,方便找出多余逗号、缺失引号等问题。字段类型错误则由应用自己给出字段名和预期类型。找不到文件与格式错误是两种不同情况,分开提示更方便排查。
入口处校验。 校验后的返回值具有固定的三个键,后续代码可以按这个结构使用。这里没有处理未知字段、重复键、文件过大或编码错误;如果配置来自不可信来源,需要按业务要求增加大小限制、未知字段策略和更完整的异常处理。json.load() 的任务是解析,业务规则仍应由应用明确规定。
- 点赞
- 收藏
- 关注作者
评论(0)