Ajv如何在自定义关键字中使用$data引用?
Great question! I’ve faced this exact issue with Ajv custom keywords before—since Ajv only natively supports $data for its built-in keywords, you do need to handle the resolution manually in your custom keyword’s validation logic. Your initial idea of using the validation function’s parameters to extract the referenced value is totally valid, and there’s a clean, reliable way to implement it using Ajv’s built-in utilities.
Here’s a step-by-step solution:
Ensure
$datais enabled in your Ajv instance
First, you must initialize Ajv with the$dataoption set totrue—this enables the internal utilities needed to resolve$datareferences.Modify your custom
rangekeyword to resolve$datareferences
In your keyword’s validation function, check if the schema is a$datareference. If it is, use Ajv’sresolveDatamethod to fetch the actual range configuration from the target field. Then proceed with your standard range validation logic.
Example Code:
const Ajv = require("ajv"); const ajv = new Ajv({ $data: true }); // Critical: Enable $data support ajv.addKeyword({ keyword: "range", type: "number", schemaType: "any", // Allow both direct range objects and $data references validate: function (schema, data, _, __, ___, ____, rootData) { // Resolve $data reference if present let rangeConfig = schema; if (schema.$data) { // Use Ajv's built-in resolveData to handle path resolution rangeConfig = ajv.resolveData(rootData, schema.$data); // Handle cases where the referenced field doesn't exist if (rangeConfig === undefined) { return false; // Adjust this behavior based on your requirements } // Optional: Validate the resolved config matches expected structure if (typeof rangeConfig !== "object" || rangeConfig === null) { return false; } } // Run your original range validation logic const { min, max } = rangeConfig; if (min !== undefined && data < min) return false; if (max !== undefined && data > max) return false; return true; }, errors: true, messages: { range: "Value must be between {{schema.min}} and {{schema.max}}" } });
How to Use This:
Define your schema with a $data reference for the range keyword, and validate your data as usual:
// Sample data const inputData = { allowedRange: { min: 10, max: 20 }, measurement: 15 }; // Schema with $data reference const validationSchema = { type: "object", properties: { allowedRange: { type: "object", properties: { min: { type: "number" }, max: { type: "number" } }, required: ["min", "max"] }, measurement: { type: "number", range: { $data: "1/allowedRange" } // Reference parent's allowedRange field } } }; // Validate const validate = ajv.compile(validationSchema); const isValid = validate(inputData); console.log(isValid); // true console.log(validate.errors); // null (no errors)
Key Notes:
- Use
resolveDatainstead of manual path parsing: Ajv’sresolveDatahandles all the edge cases of$datapath syntax (like0/for current level,1/for parent level, nested paths, etc.) so you don’t have to reinvent the wheel. - Handle missing references: Decide how you want to handle cases where the referenced field doesn’t exist (return
false, default to an open range, etc.) based on your use case. - Validate resolved config structure: Adding a check that the resolved
rangeConfigis an object ensures your validation logic doesn’t fail unexpectedly if the referenced field has the wrong type.
Your initial approach to leverage the validation function’s parameters was on the right track—this solution just formalizes it using Ajv’s built-in tools to ensure robustness.
内容的提问来源于stack exchange,提问作者Hitesh Kumar




