如何在JSON Schema中基于同层级category属性动态校验subcategory的枚举范围?
动态关联分类与子分类的JSON Schema校验方案(Node.js环境)
针对你遇到的「分类规模大,不想用大量if-else实现分类与子分类的关联校验」问题,我推荐使用ajv这个Node.js下最流行的JSON Schema验证库来解决,它支持动态生成Schema,完美适配你的场景。
核心思路
既然每个子分类仅属于一个主分类,我们可以先构建「分类-子分类」的映射关系,再基于这个映射动态生成JSON Schema的约束规则,避免手动编写大量重复的if-else逻辑。
具体实现步骤
1. 安装依赖
首先安装ajv:
npm install ajv
2. 构建分类-子分类映射
利用你已有的getCategories()和getSubcategories(category)函数,先同步(或异步)获取并构建映射关系:
const Ajv = require('ajv'); const ajv = new Ajv(); // 构建分类与子分类的映射表 const categorySubMap = {}; const categories = getCategories(); // 假设是同步函数,异步场景看后面补充 categories.forEach(category => { categorySubMap[category] = getSubcategories(category); });
3. 动态生成JSON Schema
我们会生成一个包含oneOf关键字的Schema,oneOf数组中的每个元素对应一个分类的约束(指定该分类下允许的子分类),全程自动生成,无需手动维护:
// 动态生成每个分类对应的约束规则 const categoryConstraints = Object.entries(categorySubMap).map(([category, subcategories]) => ({ properties: { category: { const: category }, // 固定当前分类 subcategory: { enum: subcategories } // 仅允许该分类下的子分类 } })); // 完整的JSON Schema const schema = { type: "array", items: { type: "object", properties: { category: { type: "string", enum: Object.keys(categorySubMap) // 约束分类必须是合法值 }, subcategory: { type: "string" } }, required: ["category", "subcategory"], // 必填字段 oneOf: categoryConstraints // 确保分类与子分类的组合合法 } };
4. 编译并验证数据
用ajv编译Schema后,就可以对目标JSON进行校验了:
// 编译验证器 const validate = ajv.compile(schema); // 测试示例数据 const validData = [ { category: "A", subcategory: "A1" }, { category: "B", subcategory: "B2" } ]; const invalidData = [ { category: "A", subcategory: "B1" } // 子分类不属于A,会校验失败 ]; console.log(validate(validData)); // 输出: true console.log(validate(invalidData)); // 输出: false console.log(validate.errors); // 会详细提示错误原因,比如subcategory不在A的允许列表中
异步场景适配
如果你的getCategories()或getSubcategories(category)是异步函数(比如从数据库/API获取数据),只需要用async/await调整代码即可:
async function createValidator() { const Ajv = require('ajv'); const ajv = new Ajv(); // 异步获取分类列表 const categories = await getCategories(); const categorySubMap = {}; // 异步获取每个分类的子分类 for (const category of categories) { categorySubMap[category] = await getSubcategories(category); } // 后续生成Schema、编译验证器的逻辑和同步场景一致 const categoryConstraints = Object.entries(categorySubMap).map(([cat, subs]) => ({ properties: { category: { const: cat }, subcategory: { enum: subs } } })); const schema = { /* 同同步场景的Schema */ }; return ajv.compile(schema); } // 使用验证器 createValidator().then(validate => { const testData = [{ category: "A", subcategory: "A2" }]; console.log(validate(testData)); // 输出: true });
方案优势
- 无需手动维护大量if-else:不管分类数量增加多少,只要你的
getCategories()和getSubcategories()能正确返回数据,代码无需修改。 - 性能优异:ajv编译后的验证器执行效率极高,适合大规模数据校验。
- 错误提示清晰:校验失败时会返回详细的错误信息,便于定位问题。
内容的提问来源于stack exchange,提问作者itasahobby
相关产品推荐
相关产品推荐

