如何基于JSON Schema属性值应用不同子Schema?(AJV+TypeScript)
基于AJV实现依赖于type字段的动态JSON验证(TypeScript + Node.js)
完全可行,AJV原生支持这种基于其他字段值的条件验证,可以通过两种常用方案实现你的需求:
方案1:使用if/then/else关键字
这种方式适合type类型较少的场景,通过判断type字段的值,动态约束fields的结构,同时用additionalProperties: false严格限制仅允许指定字段存在。
示例代码
首先定义AJV schema:
import Ajv from "ajv"; const ajv = new Ajv({ strict: true }); const schema = { type: "object", required: ["type", "fields"], properties: { type: { type: "string", enum: ["GPS", "ROOM"] }, fields: { type: "object" } }, // 当type为GPS时的约束 if: { properties: { type: { const: "GPS" } } }, then: { properties: { fields: { type: "object", required: ["lat", "lng"], properties: { lat: { type: "number" }, lng: { type: "number" } }, additionalProperties: false // 禁止fields出现其他字段 } } }, // 当type为ROOM时的约束 else: { if: { properties: { type: { const: "ROOM" } } }, then: { properties: { fields: { type: "object", required: ["temp", "humidity"], properties: { temp: { type: "number" }, humidity: { type: "number" } }, additionalProperties: false } } } } }; const validate = ajv.compile(schema);
验证测试:
// 合法的GPS数据 const gpsData = { "type": "GPS", "fields": { "lat": 51.234, "lng": -1.5434 } }; console.log(validate(gpsData)); // true // 非法的GPS数据(包含temp字段) const invalidGpsData = { "type": "GPS", "fields": { "lat": 51.234, "lng": -1.5434, "temp": 22.5 } }; console.log(validate(invalidGpsData)); // false console.log(validate.errors); // 会提示fields不允许有temp属性
方案2:使用oneOf关键字
当type类型较多时,oneOf会更清晰,每个type对应一个独立的子schema,确保只有其中一个匹配。
示例代码
const schema = { type: "object", required: ["type", "fields"], oneOf: [ // GPS类型的完整约束 { properties: { type: { const: "GPS" }, fields: { type: "object", required: ["lat", "lng"], properties: { lat: { type: "number" }, lng: { type: "number" } }, additionalProperties: false } } }, // ROOM类型的完整约束 { properties: { type: { const: "ROOM" }, fields: { type: "object", required: ["temp", "humidity"], properties: { temp: { type: "number" }, humidity: { type: "number" } }, additionalProperties: false } } } ] }; const validate = ajv.compile(schema);
结合TypeScript类型定义
为了让TypeScript类型和AJV验证逻辑对齐,可以使用区分联合类型:
type GpsFields = { lat: number; lng: number; }; type RoomFields = { temp: number; humidity: number; }; type SensorData = | { type: "GPS"; fields: GpsFields } | { type: "ROOM"; fields: RoomFields }; // 使用时会自动根据type推断fields的类型 const processData = (data: SensorData) => { if (data.type === "GPS") { console.log(data.fields.lat); // TypeScript能识别lat和lng } else { console.log(data.fields.temp); // TypeScript能识别temp和humidity } };
注意事项
- 必须设置
additionalProperties: false才能严格禁止fields出现未定义的字段; - AJV的
strict: true模式会启用更严格的校验规则,建议开启; - 如果type类型后续会扩展,
oneOf方案更容易维护,新增类型只需添加对应的子schema即可。
内容的提问来源于stack exchange,提问作者Mark Tyers
相关产品推荐
相关产品推荐

