当前位置: 首页 > 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.jsqmd.com/news/1022298/

相关文章:

  • TX3E/FMRX3MS 二功能遥控车IC+内置马达驱动
  • JESD204B线速率计算与FPGA高速接口设计实战指南
  • Splashtop远程桌面核心技术解析:低延迟图形传输与实战应用
  • LX Music Desktop:免费开源跨平台音乐播放器的完整使用指南
  • 2026年杭州GEO源头厂家权威测评:十大品牌避坑选型指南 - 品牌报告
  • 2026年6月16日海安改灯本地走访记:施工环境、密封和调光先核对哪几项 - Ayu8888
  • 水泥彩瓦厂家推荐排行榜单|2026 靠谱屋面瓦厂商整理,别墅自建房采购参考 - 商业新知
  • 深入解析PXD10 LINFlex模块:LIN总线硬件加速与寄存器配置实战
  • 终极指南:如何用BepInEx框架为Unity游戏打造强大的插件系统
  • 2026如皋防水补漏机构甄选榜单|住建实测全域靠谱修缮品牌TOP5及片区避坑指南 - 宅安选房屋修缮
  • 石家庄摄影学校哪家好?专业摄影培训认准莫瑶影视教育 - 职业学校推荐官
  • 从渗透测试视角复盘:若依(RuoYi)框架的几类常见未授权访问漏洞与实战利用
  • 2026湛江市黄金回收白银回收铂金回收彩金回收TOP5权威榜单:正规靠谱门店实地考察,高性价比首选+联系方式推荐 - 前途无量YY
  • Visio替代方案全解析:从破解风险到合法高效绘图工具
  • MPC8360E I2C与UART协议深度解析:从寄存器配置到中断编程实战
  • 2026 成都名牌包无损回收 爱马仕香奈儿 LV 迪奥古驰优选实体门店 - 开心测评
  • 手把手教你排查logback-spring.xml配置:从‘no applicable action’错误到正确使用TimeBasedRollingPolicy
  • 2026年6月静压式液位计品牌竞争力与口碑榜单:国产头部阵营技术与应用深度解析 - 仪表品牌排行榜
  • Sqribble:面向内容创作者的自动化文档操作系统
  • RAG与Agent的结合:解决幻觉问题的终极方案
  • 2026白银旧金铂金白银回收高信赖门店 TOP 线下实体商家电话与门店地址一览 - 诚金汇钻回收公司
  • 2026大兴安岭旧金铂金白银回收高信赖门店 TOP 线下实体商家电话与门店地址一览 - 诚金汇钻回收公司
  • LLM 推理加速:从算子融合到投机解码的工程实践
  • 单体应用架构设计:当微服务不是唯一解时的工程选择
  • BepInEx技术方案:解决Unity多运行时插件框架的统一架构实战
  • SpringBoot核心原理剖析:自动配置与起步依赖
  • 2026丹东旧金铂金白银回收高信赖门店 TOP 线下实体商家电话与门店地址一览 - 诚金汇钻回收公司
  • 如何深度优化显卡性能:5个高级配置方案实战解析
  • 英伟达:AXPO缩小智能体思维行动差距
  • 学位重要性下降、AI 制造 AI 正在发生!罗福莉等五位顶尖学者谈 AI 自进化与 AGI 临界点