Reputation: 18921
Let's say that I need to validate the shipping address of an order:
{ "id": 1
, "recipient": "John Doe"
, "shipping_address": [ "address line 1"
, "address line 2"
, "address line 3"
]
}
The rules for an address are:
I can implement 1 & 2 with the following JSON Schema:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
}
Question: How do I implement condition 3 with JSON Schema / Ajv?
Upvotes: 0
Views: 3923
Reputation: 18921
I found a solution using Ajv custom keywords to design the following schema:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}
The trick is to add support for maxCombinedLength
in Ajv:
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data) => {
return data.reduce((l, s) => l + s.length, 0) <= schema;
}
});
Where:
ajv
is an instance of Ajvschema
is 50
data
is the shipping_address
arrayDEMO
const validate =
ajv.compile({ type: 'array'
, items: { type: 'string' }
, minItems: 1
, maxCombinedLength: 50
});
console.log(
validate([]));
// false (need at least one item)
console.log(
validate([ "address line 1"
, "address line 2"
, "address line 3"
]));
// true
console.log(
validate([ "address line 1"
, "address line 2"
, "address line 3"
, "address line 4"
, "address line 5"
, "address line 6"
, "address line 7"
, "address line 8"
])
, validate.errors);
// false (maxCombinedLength not satisfied!)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ajv/6.12.0/ajv.min.js"></script>
<script>
const ajv = new Ajv;
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data, parent_schema) => {
return data.reduce((l, s) => l + s.length, 0) <= schema;
}
});
</script>
APPENDIX: How to ignore non-string arrays?
Obviously we can't use maxCombinedLength
with an array of objects for example.
Thankfully Ajv allows us to get access to the parent schema:
ajv.addKeyword("maxCombinedLength", {
validate: (schema, data, parent_schema) => {
if (parent_schema.items.type === 'string') {
return data.reduce((l, s) => l + s.length, 0) <= schema;
} else {
return true;
}
}
});
So with the following schema:
{ "type": "array"
, "items": { "type": "string" }
, "minItems": 1
, "maxCombinedLength": 50
}
The custom keyword function will receive 50
as the schema
parameter, the array as the data
parameter and the full schema as the parent_schema
parameter.
The parent_schema
parameter is used to query the type of the array. If we're not expecting strings we can nullify the maxCombinedLength
keyword by returning true
.
Upvotes: 2