unevaluatedItems
This commit is contained in:
Родитель
1db7a6cc2b
Коммит
b29e9b2b5d
|
@ -55,6 +55,10 @@ export interface JSONSchema {
|
||||||
|
|
||||||
// schema 2019-09
|
// schema 2019-09
|
||||||
unevaluatedProperties?: boolean | JSONSchemaRef;
|
unevaluatedProperties?: boolean | JSONSchemaRef;
|
||||||
|
unevaluatedItems?: boolean | JSONSchemaRef;
|
||||||
|
|
||||||
|
// schema 2020-12
|
||||||
|
prefixItems?: JSONSchemaRef[];
|
||||||
|
|
||||||
// VSCode extensions
|
// VSCode extensions
|
||||||
|
|
||||||
|
|
|
@ -714,53 +714,66 @@ function validate(n: ASTNode | undefined, schema: JSONSchema, validationResult:
|
||||||
|
|
||||||
}
|
}
|
||||||
function _validateArrayNode(node: ArrayASTNode): void {
|
function _validateArrayNode(node: ArrayASTNode): void {
|
||||||
if (Array.isArray(schema.items)) {
|
const prefixItemsSchemas = Array.isArray(schema.items) ? schema.items : schema.prefixItems;
|
||||||
const subSchemas = schema.items;
|
const additionalItemSchema = schema.items && !Array.isArray(schema.items) ? schema.items : schema.additionalItems;
|
||||||
for (let index = 0; index < subSchemas.length; index++) {
|
if (prefixItemsSchemas !== undefined) {
|
||||||
const subSchemaRef = subSchemas[index];
|
const max = Math.min(prefixItemsSchemas.length, node.items.length);
|
||||||
|
for (let index = 0; index < max; index++) {
|
||||||
|
const subSchemaRef = prefixItemsSchemas[index];
|
||||||
const subSchema = asSchema(subSchemaRef);
|
const subSchema = asSchema(subSchemaRef);
|
||||||
const itemValidationResult = new ValidationResult();
|
const itemValidationResult = new ValidationResult();
|
||||||
const item = node.items[index];
|
const item = node.items[index];
|
||||||
if (item) {
|
if (item) {
|
||||||
validate(item, subSchema, itemValidationResult, matchingSchemas);
|
validate(item, subSchema, itemValidationResult, matchingSchemas);
|
||||||
validationResult.mergePropertyMatch(itemValidationResult);
|
validationResult.mergePropertyMatch(itemValidationResult);
|
||||||
} else if (node.items.length >= subSchemas.length) {
|
|
||||||
validationResult.propertiesValueMatches++;
|
|
||||||
}
|
}
|
||||||
|
validationResult.processedProperties.add(String(index));
|
||||||
}
|
}
|
||||||
if (node.items.length > subSchemas.length) {
|
if (node.items.length > prefixItemsSchemas.length) {
|
||||||
if (typeof schema.additionalItems === 'object') {
|
if (additionalItemSchema !== undefined) {
|
||||||
for (let i = subSchemas.length; i < node.items.length; i++) {
|
if (typeof additionalItemSchema === 'object') {
|
||||||
const itemValidationResult = new ValidationResult();
|
for (let i = prefixItemsSchemas.length; i < node.items.length; i++) {
|
||||||
validate(node.items[i], <any>schema.additionalItems, itemValidationResult, matchingSchemas);
|
const itemValidationResult = new ValidationResult();
|
||||||
validationResult.mergePropertyMatch(itemValidationResult);
|
validate(node.items[i], <any>schema.additionalItems, itemValidationResult, matchingSchemas);
|
||||||
|
validationResult.mergePropertyMatch(itemValidationResult);
|
||||||
|
}
|
||||||
|
} else if (additionalItemSchema === false) {
|
||||||
|
validationResult.problems.push({
|
||||||
|
location: { offset: node.offset, length: node.length },
|
||||||
|
message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer.', subSchemas.length)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (let i = prefixItemsSchemas.length; i < node.items.length; i++) {
|
||||||
|
validationResult.processedProperties.add(String(i));
|
||||||
|
validationResult.propertiesValueMatches++;
|
||||||
}
|
}
|
||||||
} else if (schema.additionalItems === false) {
|
|
||||||
validationResult.problems.push({
|
|
||||||
location: { offset: node.offset, length: node.length },
|
|
||||||
message: localize('additionalItemsWarning', 'Array has too many items according to schema. Expected {0} or fewer.', subSchemas.length)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (additionalItemSchema) {
|
||||||
const itemSchema = asSchema(schema.items);
|
const itemSchema = asSchema(additionalItemSchema);
|
||||||
if (itemSchema) {
|
if (itemSchema) {
|
||||||
for (const item of node.items) {
|
for (let index = 0; index < node.items.length; index++) {
|
||||||
|
const item = node.items[index];
|
||||||
const itemValidationResult = new ValidationResult();
|
const itemValidationResult = new ValidationResult();
|
||||||
validate(item, itemSchema, itemValidationResult, matchingSchemas);
|
validate(item, itemSchema, itemValidationResult, matchingSchemas);
|
||||||
validationResult.mergePropertyMatch(itemValidationResult);
|
validationResult.mergePropertyMatch(itemValidationResult);
|
||||||
|
validationResult.processedProperties.add(String(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const containsSchema = asSchema(schema.contains);
|
const containsSchema = asSchema(schema.contains);
|
||||||
if (containsSchema) {
|
if (containsSchema) {
|
||||||
const doesContain = node.items.some(item => {
|
let doesContain = false;
|
||||||
|
for (let index = 0; index < node.items.length; index++) {
|
||||||
|
const item = node.items[index];
|
||||||
const itemValidationResult = new ValidationResult();
|
const itemValidationResult = new ValidationResult();
|
||||||
validate(item, containsSchema, itemValidationResult, NoOpSchemaCollector.instance);
|
validate(item, containsSchema, itemValidationResult, NoOpSchemaCollector.instance);
|
||||||
return !itemValidationResult.hasProblems();
|
if (!itemValidationResult.hasProblems()) {
|
||||||
});
|
doesContain = true;
|
||||||
|
validationResult.processedProperties.add(String(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!doesContain) {
|
if (!doesContain) {
|
||||||
validationResult.problems.push({
|
validationResult.problems.push({
|
||||||
location: { offset: node.offset, length: node.length },
|
location: { offset: node.offset, length: node.length },
|
||||||
|
@ -769,6 +782,26 @@ function validate(n: ASTNode | undefined, schema: JSONSchema, validationResult:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const unevaluatedItems = schema.unevaluatedItems;
|
||||||
|
if (unevaluatedItems !== undefined) {
|
||||||
|
for (let i = 0; i < node.items.length; i++) {
|
||||||
|
if (!validationResult.processedProperties.has(String(i))) {
|
||||||
|
if (unevaluatedItems === false) {
|
||||||
|
validationResult.problems.push({
|
||||||
|
location: { offset: node.offset, length: node.length },
|
||||||
|
message: localize('unevaluatedItemsWarning', 'Item does not match any validation rule from the array.')
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const itemValidationResult = new ValidationResult();
|
||||||
|
validate(node.items[i], <any>schema.additionalItems, itemValidationResult, matchingSchemas);
|
||||||
|
validationResult.mergePropertyMatch(itemValidationResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validationResult.processedProperties.add(String(i));
|
||||||
|
validationResult.propertiesValueMatches++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isNumber(schema.minItems) && node.items.length < schema.minItems) {
|
if (isNumber(schema.minItems) && node.items.length < schema.minItems) {
|
||||||
validationResult.problems.push({
|
validationResult.problems.push({
|
||||||
location: { offset: node.offset, length: node.length },
|
location: { offset: node.offset, length: node.length },
|
||||||
|
|
|
@ -1916,6 +1916,87 @@ suite('JSON Parser', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.only('unevaluatedItems', function () {
|
||||||
|
let schema: JSONSchema = {
|
||||||
|
type: 'array',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'integer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'boolean'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
unevaluatedItems: false
|
||||||
|
};
|
||||||
|
{
|
||||||
|
const { textDoc, jsonDoc } = toDocument('[1, true]');
|
||||||
|
|
||||||
|
const semanticErrors = jsonDoc.validate(textDoc, schema);
|
||||||
|
assert.strictEqual(semanticErrors!.length, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const { textDoc, jsonDoc } = toDocument('[1, true, "string", 42]');
|
||||||
|
|
||||||
|
const semanticErrors = jsonDoc.validate(textDoc, schema);
|
||||||
|
assert.strictEqual(semanticErrors!.length, 2);
|
||||||
|
}
|
||||||
|
schema = {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'integer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'integer'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
type: 'integer'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'boolean'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'boolean'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
unevaluatedItems: false
|
||||||
|
};
|
||||||
|
{
|
||||||
|
const { textDoc, jsonDoc } = toDocument('[1, 1]');
|
||||||
|
|
||||||
|
const semanticErrors = jsonDoc.validate(textDoc, schema);
|
||||||
|
assert.strictEqual(semanticErrors!.length, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const { textDoc, jsonDoc } = toDocument('[1, true, true]');
|
||||||
|
|
||||||
|
const semanticErrors = jsonDoc.validate(textDoc, schema);
|
||||||
|
assert.strictEqual(semanticErrors!.length, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const { textDoc, jsonDoc } = toDocument('[1, true, true, true, "Hello"]');
|
||||||
|
|
||||||
|
const semanticErrors = jsonDoc.validate(textDoc, schema);
|
||||||
|
assert.strictEqual(semanticErrors!.length, 2);
|
||||||
|
}
|
||||||
|
schema = {
|
||||||
|
"type": "array",
|
||||||
|
"prefixItems": [{ "type": "string" }, { "type": "string" }],
|
||||||
|
"contains": { "type": "string", "minLength": 3 },
|
||||||
|
"unevaluatedItems": false
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
test('multipleOf', function () {
|
test('multipleOf', function () {
|
||||||
const schema: JSONSchema = {
|
const schema: JSONSchema = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
|
Загрузка…
Ссылка в новой задаче