/// <summary> /// Provides the validation logic for this keyword. /// </summary> /// <param name="context">The context object.</param> /// <returns>Results object containing a final result and any errors that may have been found.</returns> public SchemaValidationResults Validate(SchemaValidationContext context) { if (context.Instance.Type != JsonValueType.Array) { return(new SchemaValidationResults(Name, context)); } var itemsKeyword = context.Local.Get <ItemsKeyword?>(); if (itemsKeyword == null || !itemsKeyword.IsArray) { return(new SchemaValidationResults(Name, context)); } var nestedResults = new List <SchemaValidationResults>(); var array = context.Instance.Array; var results = new SchemaValidationResults(Name, context); var valid = true; var reportChildErrors = context.Options.ShouldReportChildErrors(this, context); var startIndex = context.LocalTierLastEvaluatedIndex + 1; var failedIndices = new JsonArray(); Log.Schema(() => startIndex == 0 ? "No indices have been evaluated; process all" : $"Indices up to {context.LastEvaluatedIndex} have been evaluated; skipping these"); if (startIndex < array.Count) { if (Value == JsonSchema.False) { Log.Schema(() => $"Subschema is `false`; all instances after index {startIndex} are invalid"); results.IsValid = false; results.Keyword = Name; results.AdditionalInfo["indices"] = Enumerable.Range(startIndex, array.Count - startIndex).ToJson(); results.ErrorMessage = ErrorTemplate_False.ResolveTokens(results.AdditionalInfo); return(results); } var eligibleItems = array.Skip(startIndex); var index = startIndex; foreach (var item in eligibleItems) { var baseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name); var relativeLocation = context.RelativeLocation.CloneAndAppend(Name); var newContext = new SchemaValidationContext(context) { Instance = item, BaseRelativeLocation = baseRelativeLocation, RelativeLocation = relativeLocation, InstanceLocation = context.InstanceLocation.CloneAndAppend(index.ToString()), }; var localResults = Value.Validate(newContext); if (!localResults.IsValid) { failedIndices.Add(index); } else if (context.ShouldTrackValidatedValues) { newContext.LocallyValidatedIndices.Add(index); } valid &= localResults.IsValid; context.LastEvaluatedIndex = Math.Max(context.LastEvaluatedIndex, index); context.LocalTierLastEvaluatedIndex = Math.Max(context.LastEvaluatedIndex, index); context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext); index++; if (context.Options.OutputFormat == SchemaValidationOutputFormat.Flag) { if (!valid) { Log.Schema(() => "Subschema failed; halting validation early"); break; } } else if (reportChildErrors) { nestedResults.Add(localResults); } } } else { Log.Schema(() => "All items have been validated"); } results.NestedResults = nestedResults; results.IsValid = valid; results.Keyword = Name; if (!valid) { results.AdditionalInfo["indices"] = failedIndices; results.ErrorMessage = ErrorTemplate.ResolveTokens(results.AdditionalInfo); } return(results); }
/// <summary> /// Provides the validation logic for this keyword. /// </summary> /// <param name="context">The context object.</param> /// <returns>Results object containing a final result and any errors that may have been found.</returns> public SchemaValidationResults Validate(SchemaValidationContext context) { if (context.Instance.Type != JsonValueType.Object) { Log.Schema(() => "Instance not an object; not applicable"); return(new SchemaValidationResults(Name, context)); } var obj = context.Instance.Object; var results = new SchemaValidationResults(Name, context); var toEvaluate = obj.Where(kvp => !context.LocallyEvaluatedPropertyNames.Contains(kvp.Key)) !.ToJson(); if (toEvaluate.Count == 0) { Log.Schema(() => "All properties have been evaluated"); return(results); } Log.Schema(() => context.LocallyEvaluatedPropertyNames.Count == 0 ? "No properties have been evaluated; process all" : $"Properties {context.LocallyEvaluatedPropertyNames.ToJson()} have been evaluated; skipping these"); if (Value == JsonSchema.False && toEvaluate.Any()) { Log.Schema(() => "Subschema is `false`; all instances invalid"); results.IsValid = false; results.Keyword = Name; results.AdditionalInfo["properties"] = toEvaluate.Keys.ToJson(); results.ErrorMessage = ErrorTemplate_False.ResolveTokens(results.AdditionalInfo); return(results); } var valid = true; var reportChildErrors = context.Options.ShouldReportChildErrors(this, context); var nestedResults = new List <SchemaValidationResults>(); var failedProperties = new JsonArray(); foreach (var kvp in toEvaluate) { if (context.ShouldTrackValidatedValues) { context.EvaluatedPropertyNames.Add(kvp.Key); } context.LocallyEvaluatedPropertyNames.Add(kvp.Key); var newContext = new SchemaValidationContext(context) { Instance = kvp.Value, BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name), RelativeLocation = context.RelativeLocation.CloneAndAppend(Name), InstanceLocation = context.InstanceLocation.CloneAndAppend(kvp.Key), }; var localResults = Value.Validate(newContext); if (!localResults.IsValid) { failedProperties.Add(kvp.Key); } else { if (context.ShouldTrackValidatedValues) { newContext.LocallyEvaluatedPropertyNames.Add(kvp.Key); } context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext); } valid &= localResults.IsValid; if (context.Options.OutputFormat == SchemaValidationOutputFormat.Flag) { if (!valid) { Log.Schema(() => "Subschema failed; halting validation early"); break; } } else if (reportChildErrors) { nestedResults.Add(localResults); } } results.NestedResults = nestedResults; if (!valid || nestedResults.Any(r => !r.IsValid)) { results.IsValid = false; results.Keyword = Name; results.AdditionalInfo["properties"] = failedProperties; results.ErrorMessage = ErrorTemplate.ResolveTokens(results.AdditionalInfo); } return(results); }