如何基于外部JSON文件有效值验证Region JSON Schema的Country属性
好的,我来帮你解决这个问题——你想要定义region的JSON Schema,让其中的countries属性自动关联countries.json里的国家code,又不想手动重复枚举列表对吧?这里有两种靠谱的方案,你可以根据自己的场景选择:
方案1:动态生成可复用的枚举Schema(推荐,兼容性强)
JSON Schema本身没办法直接读取外部JSON实例(比如countries.json)的值来做验证,但我们可以用脚本动态生成一个包含国家code枚举的Schema片段,然后在country和region的Schema里都引用它,这样就不用手动维护重复的列表了。
步骤1:写脚本生成枚举Schema
用Node.js写个简单的脚本(其他语言也可以,逻辑一样),读取countries.json并提取所有code值,生成一个独立的枚举Schema文件:
const fs = require('fs'); // 读取countries.json里的国家列表 const countries = JSON.parse(fs.readFileSync('./countries.json', 'utf8')); // 提取所有国家code const countryCodes = countries.map(country => country.code); // 生成枚举Schema const enumSchema = { "$id": "https://example.com/country-codes-enum.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "type": "string", "enum": countryCodes }; // 写入文件 fs.writeFileSync('./country-codes-enum.schema.json', JSON.stringify(enumSchema, null, 2));
步骤2:重构原有的Country Schema
把原来的country数组Schema改成引用这个枚举片段,确保国家code的验证和region的验证用的是同一套规则:
{ "$id": "https://example.com/countries.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A list of countries", "type": "array", "items": { "type": "object", "required": ["code", "name"], "properties": { "code": { "$ref": "country-codes-enum.schema.json" }, "name": { "type": "string" } } } }
步骤3:定义Region Schema
现在region的Schema就可以直接引用这个枚举片段来验证countries数组的元素了,完全不用手动写枚举:
{ "$id": "https://example.com/regions.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A list of regions", "type": "array", "items": { "type": "object", "required": ["name", "countries"], "properties": { "name": { "type": "string" }, "countries": { "type": "array", "items": { "$ref": "country-codes-enum.schema.json" }, "uniqueItems": true // 可选:防止同一个region里重复添加同一个国家code } } } }
以后只要countries.json更新了,重新跑一遍脚本生成新的枚举Schema就行,两个主Schema都会自动用上最新的国家列表。
方案2:用支持$data关键字的验证器(比如Ajv)
如果你的验证工具支持JSON Schema Draft 07的$data关键字(比如Ajv这个常用的JS验证库),可以直接在Region Schema里引用countries.json的实例数据,不用额外生成枚举文件。
步骤1:定义Region Schema
直接用$data指向countries.json里的code值路径(路径语法要对应验证器的规则):
{ "$id": "https://example.com/regions.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "A list of regions", "type": "array", "items": { "type": "object", "required": ["name", "countries"], "properties": { "name": { "type": "string" }, "countries": { "type": "array", "items": { "type": "string", "minLength": 2, "maxLength": 2, "$data": "1/countries/*/code" // 路径对应验证上下文里的countries数据结构 }, "uniqueItems": true } } } }
步骤2:用Ajv验证时传入上下文数据
在验证region数据的时候,把countries.json的数据作为上下文传入Ajv:
const Ajv = require('ajv'); const ajv = new Ajv({ $data: true }); // 一定要启用$data支持 const fs = require('fs'); // 加载Schema和数据 const regionSchema = JSON.parse(fs.readFileSync('./regions.schema.json', 'utf8')); const countriesData = JSON.parse(fs.readFileSync('./countries.json', 'utf8')); const regionData = [ { "name": "Europe", "countries": ["GB"] }, { "name": "North America", "countries": ["US"] } ]; // 编译验证函数并验证 const validate = ajv.compile(regionSchema); const isValid = validate(regionData, { countries: countriesData }); if (!isValid) { console.error('验证失败:', validate.errors); } else { console.log('验证通过!'); }
这种方法不用生成额外文件,但依赖验证器对$data的支持,兼容性不如方案1。
内容的提问来源于stack exchange,提问作者jwa

