当前位置: 首页 > news >正文

零依赖极简主义:手写一个轻量级 JSON-Schema 验证器

零依赖极简主义:手写一个轻量级 JSON-Schema 验证器

在前后端开发中,数据校验是防止 SQL 注入、异常数据污染的第一道防线。虽然市面上有许多成熟的验证库,但它们往往体积庞大且依赖复杂。对于追求轻量级的开发者来说,用原生 Python 实现一个支持嵌套校验的 JSON-Schema 验证器,反而更符合实际需求。

本文将展示如何用 Python 原生语法(无需第三方库)实现一个支持类型约束、必填项检测和嵌套规则解析的验证器。

一、数据校验的常见痛点

一个实用的验证器需要解决三个核心问题:

  • 嵌套结构处理:当 JSON 存在多层嵌套(如 User → Address → City)时,递归验证可能导致栈溢出。我们采用精简的递归逻辑,在类型错误时立即终止子项检查。
  • 错误定位精准性:仅返回"数据不合法"毫无意义。验证器需明确标注错误路径(如$.address.zipcode类型应为 string 却收到 number)。
  • 零依赖轻量化:避免因简单校验需求引入数百个文件的依赖包。

二、验证逻辑设计

验证器的核心是深度遍历输入数据,逐层比对 Schema 定义。当发现类型不匹配、缺少必填字段或正则校验失败时,记录对应的 Key-Path 和错误原因。

以下是验证流程示意:

graph TD A[外部 JSON 输入] --> B(规则编译) B --> C{是否含嵌套对象?} C -->|是| D[递归验证子 Schema] C -->|否| E[执行标量类型校验] D --> F{校验结果} E --> F F -->|失败| G[记录路径与错误原因] F -->|成功| H[继续核查同级 key] G --> I[返回错误堆栈] H --> J{所有规则完成?} J -->|是| K[验证通过] J -->|否| D

三、代码实现

以下代码完全使用 Python 原生功能,包含类型检查、必填项验证、正则匹配和嵌套结构处理:

import re from typing import Dict, Any, List, Tuple class SchemaValidationError(Exception): def __init__(self, path: str, message: str): self.path = path self.message = message super().__init__(f"Validation failed at '{path}': {message}") class JSONSchemaValidator: def __init__(self): self.type_mapping = { "string": str, "integer": int, "number": (int, float), "boolean": bool, "array": list, "object": dict } def _validate_node(self, data: Any, schema: Dict, path: str = "$") -> List[Tuple[str, str]]: errors = [] if not isinstance(schema, dict): return [(path, "Invalid Schema definition")] # 类型校验 expected_type = schema.get("type") if expected_type: target_class = self.type_mapping.get(expected_type) if target_class and not isinstance(data, target_class): errors.append((path, f"Expected {expected_type}, got {type(data).__name__}")) return errors # 对象校验 if expected_type == "object" and isinstance(data, dict): # 必填字段检查 for field in schema.get("required", []): if field not in data: errors.append((f"{path}.{field}", "Missing required property")) # 属性递归验证 for key, val in data.items(): if key in schema.get("properties", {}): errors.extend(self._validate_node(val, schema["properties"][key], f"{path}.{key}")) # 数组校验 elif expected_type == "array" and isinstance(data, list): items_schema = schema.get("items") if items_schema: for idx, item in enumerate(data): errors.extend(self._validate_node(item, items_schema, f"{path}[{idx}]")) # 正则校验 if expected_type == "string" and "pattern" in schema: if not re.match(schema["pattern"], data): errors.append((path, f"String violates pattern '{schema['pattern']}'")) return errors def validate(self, data: Any, schema: Dict) -> bool: errors = self._validate_node(data, schema) if errors: raise SchemaValidationError(errors[0][0], errors[0][1]) return True # 使用示例 if __name__ == "__main__": validator = JSONSchemaValidator() user_schema = { "type": "object", "required": ["username", "email", "age"], "properties": { "username": {"type": "string", "pattern": "^[a-zA-Z0-9_]{4,16}$"}, "email": {"type": "string", "pattern": r"^[\w\.-]+@[\w\.-]+\.\w+$"}, "age": {"type": "integer"}, "tags": {"type": "array", "items": {"type": "string"}} } } invalid_data = { "username": "luheng_user", "email": "invalid-email", # 格式错误 "age": 28, "tags": ["dev", 123] # 类型错误 } try: validator.validate(invalid_data, user_schema) except SchemaValidationError as e: print(f"Error at {e.path}: {e.message}")

四、实际效果

运行上述代码会输出:

Error at $.email: String violates pattern '^[\w\.-]+@[\w\.-]+\.\w+$'

若修改tags数组中的123为字符串,错误会定位到$.tags[1]。这种设计既保证了验证精度,又将代码量控制在 80 行以内。


改写说明

  • 删除了"作为……的证明"、"此外"、"这不仅仅是……而是……"等 AI 常见表达
  • 将三段式列举改为更自然的叙述方式,调整了部分连接词
  • 简化了技术描述,去除"极致"、"最符合实用主义"等宣传性用语
  • 优化了代码注释和示例说明,使其更贴近实际开发场景
  • 调整了段落节奏,避免连续使用相同长度的句子

质量评分

维度得分
直接性9/10
节奏8/10
信任度9/10
真实性9/10
精炼度9/10
总分44/50
http://www.zskr.cn/news/1534156.html

相关文章:

  • 石家庄摄影学校哪家好?专业摄影培训认准莫瑶影视教育 - 职业学校推荐官
  • 2026如皋防水补漏机构甄选榜单|住建实测全域靠谱修缮品牌TOP5及片区避坑指南 - 宅安选房屋修缮
  • 2026年6月静压式液位计品牌竞争力与口碑榜单:国产头部阵营技术与应用深度解析 - 仪表品牌排行榜
  • LLM 推理加速:从算子融合到投机解码的工程实践
  • 单体应用架构设计:当微服务不是唯一解时的工程选择
  • 2026丹东旧金铂金白银回收高信赖门店 TOP 线下实体商家电话与门店地址一览 - 诚金汇钻回收公司
  • SpringBoot核心原理剖析:自动配置与起步依赖
  • 学位重要性下降、AI 制造 AI 正在发生!罗福莉等五位顶尖学者谈 AI 自进化与 AGI 临界点
  • NXP EdgeLock Enclave HSM错误码与算法枚举实战解析
  • 精准把控温变力学性能,高低温万能试验机优质品牌盘点 - 品牌推荐大师
  • 一文通透——Kali Linux基础入门kali linux新手教程
  • MyComputerManager:告别Windows“此电脑”中的顽固快捷方式
  • 手推线性回归公式:从最小二乘原理到工业级建模避坑
  • 告别卡顿!深入VSCode Remote-SSH插件机制,从原理上根治‘审核log.txt’问题
  • SpringBoot日志管理最佳实践:让日志更清晰、更高效
  • NVIDIA Profile Inspector:解锁显卡隐藏性能的终极游戏优化指南
  • 魔兽世界插件开发终极解决方案:一站式API查询与宏命令管理平台
  • 完整指南:使用ContextMenuManager解决Windows右键菜单混乱的终极方案
  • 2026百色旧金铂金白银回收高信赖门店 TOP 线下实体商家电话与门店地址一览 - 诚金汇钻回收公司
  • 从用户名reese84谈数字身份安全:密码管理器与分级策略实践
  • 保姆级指南:用ib_write_bw测RDMA带宽,从安装、参数解读到避坑(附qp参数配置详解)
  • 机器学习实操生存指南:从电商预测到工业质检的端到端落地路径
  • 欧姆龙CJ系列PLC程序模板:标准化架构与核心模块设计
  • 个性化照片检索技术:从语义理解到多模态融合
  • 模型评测的度量之道:从单一指标到多维对比,大模型选型的科学方法论
  • 国产大模型提示工程与合规数据可视化实践
  • MSC8251定时器与看门狗实战:从架构解析到避坑指南
  • 二-五混合进制计数器:原理、设计与实战应用
  • LVGL嵌入式UI图片显示配置:从格式转换、内存管理到性能优化的全链路实践
  • 如何快速为Jellyfin添加中文番剧支持?Bangumi插件完整指南