Пример #1
0
        /// <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 (Resolved == null)
            {
                _ResolveReference(context);
                if (Resolved == null)
                {
                    throw new SchemaReferenceNotFoundException(context.RelativeLocation);
                }

                Log.Schema(() => "Reference found");
            }

            var results = new SchemaValidationResults(Name, context);

            var newContext = new SchemaValidationContext(context)
            {
                BaseUri              = _resolvedRoot?.DocumentPath,
                Instance             = context.Instance,
                Root                 = _resolvedRoot ?? context.Root,
                BaseRelativeLocation = _resolvedFragment?.WithHash(),
                RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name),
            };
            var nestedResults = Resolved.Validate(newContext);

            results.IsValid = nestedResults.IsValid;
            results.NestedResults.Add(nestedResults);
            context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);

            return(results);
        }
Пример #2
0
        /// <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)
        {
            var results = new SchemaValidationResults(Name, context);

            var newContext = new SchemaValidationContext(context)
            {
                BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name),
                RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name),
            };
            var nestedResults = Value.Validate(newContext);

            results.IsValid = !nestedResults.IsValid;
            if (nestedResults.IsValid)
            {
                context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
            }

            if (!results.IsValid)
            {
                Log.Schema(() => "Subschema succeeded; inverting result");
                results.ErrorMessage = ErrorTemplate;
            }

            if (context.Options.OutputFormat != SchemaValidationOutputFormat.Flag &&
                context.Options.ShouldReportChildErrors(this, context))
            {
                results.NestedResults = new List <SchemaValidationResults> {
                    nestedResults
                }
            }
            ;

            return(results);
        }
Пример #3
0
        /// <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)
        {
            var then  = context.Local.Get <ThenKeyword>();
            var @else = context.Local.Get <ElseKeyword>();

            if (then != null || @else != null || context.ShouldTrackValidatedValues)
            {
                var newContext = new SchemaValidationContext(context)
                {
                    BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name),
                    RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name),
                };

                var ifResults = Value.Validate(newContext);
                context.Misc["ifKeywordValid"] = ifResults.IsValid;

                if (ifResults.IsValid)
                {
                    context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
                }
            }
            else
            {
                Log.Schema(() => "`then` and `else` keywords not present; skipping `if` validation");
            }

            return(new SchemaValidationResults(Name, context));
        }
Пример #4
0
        /// <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)
        {
            var valid             = false;
            var reportChildErrors = context.Options.ShouldReportChildErrors(this, context);
            var i             = 0;
            var nestedResults = new List <SchemaValidationResults>();

            var baseContext = new SchemaValidationContext(context);

            baseContext.EvaluatedPropertyNames.Clear();
            baseContext.LocallyEvaluatedPropertyNames.Clear();
            baseContext.LastEvaluatedIndex          = -1;
            baseContext.LocalTierLastEvaluatedIndex = -1;
            foreach (var s in this)
            {
                var newContext = new SchemaValidationContext(baseContext)
                {
                    BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name, i.ToString()),
                    RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name, i.ToString()),
                };
                var localResults = s.Validate(newContext);
                valid |= localResults.IsValid;
                Log.Schema(() => $"`{Name}` {(valid ? "valid" : "invalid")} so far");
                if (localResults.IsValid)
                {
                    context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
                }

                if (context.Options.OutputFormat == SchemaValidationOutputFormat.Flag)
                {
                    if (valid)
                    {
                        Log.Schema(() => "Subschema passed; halting validation early");
                        break;
                    }
                }
                else if (reportChildErrors)
                {
                    nestedResults.Add(localResults);
                }

                i++;
            }

            var resultsList = nestedResults.ToList();
            var results     = new SchemaValidationResults(Name, context)
            {
                NestedResults = resultsList,
                IsValid       = valid
            };

            if (!results.IsValid)
            {
                results.ErrorMessage = ErrorTemplate.ResolveTokens(results.AdditionalInfo);
            }

            return(results);
        }
Пример #5
0
        /// <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)
        {
            var valid             = true;
            var reportChildErrors = context.Options.ShouldReportChildErrors(this, context);
            var i             = 0;
            var nestedResults = new List <SchemaValidationResults>();
            var failedCount   = 0;

            foreach (var s in this)
            {
                var newContext = new SchemaValidationContext(context)
                {
                    BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name, i.ToString()),
                    RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name, i.ToString()),
                };
                var localResults = s.Validate(newContext);
                valid &= localResults.IsValid;
                Log.Schema(() => $"`{Name}` {(valid ? "valid" : "invalid")} so far");
                if (!localResults.IsValid)
                {
                    failedCount++;
                }
                else
                {
                    context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
                }

                if (context.Options.OutputFormat == SchemaValidationOutputFormat.Flag)
                {
                    if (!valid)
                    {
                        Log.Schema(() => "Subschema failed; halting validation early");
                        break;
                    }
                }
                else if (reportChildErrors)
                {
                    nestedResults.Add(localResults);
                }

                i++;
            }

            var results = new SchemaValidationResults(Name, context)
            {
                NestedResults = nestedResults,
                IsValid       = valid
            };

            if (!results.IsValid)
            {
                results.AdditionalInfo["failed"] = failedCount;
                results.AdditionalInfo["total"]  = Count;
                results.ErrorMessage             = ErrorTemplate.ResolveTokens(results.AdditionalInfo);
            }

            return(results);
        }
Пример #6
0
        /// <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)
        {
            var reportChildErrors = context.Options.ShouldReportChildErrors(this, context);
            var i             = 0;
            var nestedResults = new List <SchemaValidationResults>();
            var validCount    = 0;

            var contextCopy = new SchemaValidationContext(context);

            foreach (var s in this)
            {
                var newContext = new SchemaValidationContext(context)
                {
                    BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name, i.ToString()),
                    RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name, i.ToString()),
                };
                var localResults = s.Validate(newContext);
                if (localResults.IsValid)
                {
                    validCount++;
                    contextCopy.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
                }
                Log.Schema(() => $"`{Name}` {validCount} items valid so far");

                if (context.Options.OutputFormat == SchemaValidationOutputFormat.Flag)
                {
                    if (validCount > 1)
                    {
                        Log.Schema(() => "More than one subschema succeeded; halting validation early");
                        break;
                    }
                }
                else if (reportChildErrors)
                {
                    nestedResults.Add(localResults);
                }
            }

            var results = new SchemaValidationResults(Name, context)
            {
                IsValid       = validCount == 1,
                NestedResults = nestedResults
            };

            if (!results.IsValid)
            {
                Log.Schema(() => $"{validCount} subschemas passed validation; expected only one");
                results.AdditionalInfo["passed"] = validCount;
                results.ErrorMessage             = ErrorTemplate.ResolveTokens(results.AdditionalInfo);
            }
            else
            {
                context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(contextCopy);
            }

            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.RecursiveRefValidatedLocations == null)
            {
                context.RecursiveRefValidatedLocations = new List <JsonPointer>();
            }

            if (context.RecursiveRefValidatedLocations.Any(l => Equals(l, context.InstanceLocation)))
            {
                return new SchemaValidationResults(Name, context)
                       {
                           RecursionDetected = true,
                           AnnotationValue   = "Detected recursive loop. Processing halted on this branch."
                       }
            }
            ;

            if (Resolved == null)
            {
                _ResolveReference(context);

                if (Resolved == null)
                {
                    throw new SchemaReferenceNotFoundException(context.RelativeLocation);
                }

                Log.Schema(() => "Reference found");
            }

            var results = new SchemaValidationResults(Name, context);

            var newContext = new SchemaValidationContext(context)
            {
                BaseUri = _resolvedRoot !.DocumentPath,
                Root    = _resolvedRoot ?? context.Root,
                BaseRelativeLocation = _resolvedFragment !.WithHash(),
                RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name),
            };

            context.RecursiveRefValidatedLocations.Add(context.InstanceLocation);
            var nestedResults = Resolved.Validate(newContext);

            context.RecursiveRefValidatedLocations.Remove(context.InstanceLocation);

            results.IsValid = nestedResults.IsValid;
            results.NestedResults.Add(nestedResults);
            context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);

            return(results);
        }
Пример #8
0
        /// <summary>
        /// Provides the validation logic for this dependency.
        /// </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)
        {
            var results = new SchemaValidationResults(PropertyName, context)
            {
                Keyword = $"{context.Misc["dependencyParent"]}/{PropertyName}"
            };

            if (context.Instance.Type != JsonValueType.Object)
            {
                Log.Schema(() => "Instance not an object; not applicable");
                return(results);
            }

            if (!context.Instance.Object.ContainsKey(PropertyName))
            {
                Log.Schema(() => $"Property {PropertyName} not found; not applicable");
                return(results);
            }

            var newContext = new SchemaValidationContext(context)
            {
                BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(PropertyName),
                RelativeLocation     = context.RelativeLocation.CloneAndAppend(PropertyName),
            };

            var nestedResult = _schema.Validate(newContext);

            results.NestedResults = new List <SchemaValidationResults> {
                nestedResult
            };

            if (!nestedResult.IsValid)
            {
                Log.Schema(() => $"Property {PropertyName} found, but subschema failed");
                results.IsValid      = false;
                results.ErrorMessage = ErrorTemplate;
            }
            else
            {
                if (context.ShouldTrackValidatedValues)
                {
                    newContext.LocallyEvaluatedPropertyNames.Add(PropertyName);
                }
                context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
            }

            return(results);
        }
Пример #9
0
        /// <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.Misc.TryGetValue("ifKeywordValid", out var ifKeywordValidStore))
            {
                Log.Schema(() => "`if` keyword not present; not applicable");
                return(new SchemaValidationResults(Name, context));
            }

            var ifKeywordValid = (bool)ifKeywordValidStore;

            if (!ifKeywordValid)
            {
                Log.Schema(() => "`if` subschema failed; not applicable");
                return(new SchemaValidationResults(Name, context));
            }

            var results = new SchemaValidationResults(Name, context);

            var newContext = new SchemaValidationContext(context)
            {
                BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name),
                RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name),
            };
            var thenResults = Value.Validate(newContext);

            if (thenResults.IsValid)
            {
                context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
            }
            else
            {
                Log.Schema(() => "`if` subschema succeeded, but `then` subschema failed");
                results.IsValid      = false;
                results.Keyword      = Name;
                results.ErrorMessage = ErrorTemplate;
                if (context.Options.ShouldReportChildErrors(this, context))
                {
                    results.NestedResults.Add(thenResults);
                }
            }

            return(results);
        }
Пример #10
0
        /// <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)
            {
                Log.Schema("Instance not an array; not applicable");
                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 = JsonSchemaOptions.ShouldReportChildErrors(this, context);
            var startIndex        = context.LastEvaluatedIndex + 1;

            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 invalid");
                    results.IsValid      = false;
                    results.Keyword      = Name;
                    results.ErrorMessage = ErrorTemplate;
                    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);
                    valid &= localResults.IsValid;
                    if (valid)
                    {
                        context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
                    }
                    index++;

                    if (JsonSchemaOptions.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.ErrorMessage = ErrorTemplate;
            }

            return(results);
        }
Пример #11
0
        /// <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);
        }
Пример #12
0
        /// <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)
        {
            var results = new SchemaValidationResults(Name, context);

            if (context.Instance.Type != JsonValueType.Object)
            {
                Log.Schema("Instance not an object; not applicable");
                return(results);
            }

            var nestedResults     = new List <SchemaValidationResults>();
            var obj               = context.Instance.Object;
            var reportChildErrors = JsonSchemaOptions.ShouldReportChildErrors(this, context);
            var valid             = true;

            foreach (var patternProperty in this)
            {
                var pattern     = new Regex(patternProperty.Key);
                var localSchema = patternProperty.Value;
                var matches     = obj.Keys.Where(k => pattern.IsMatch(k));
                if (matches.Any())
                {
                    Log.Schema($"Properties {matches.ToJson()} are matches for regular expression \"{pattern}\"");
                    var baseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name, patternProperty.Key);
                    var relativeLocation     = context.RelativeLocation.CloneAndAppend(Name, patternProperty.Key);
                    foreach (var match in matches)
                    {
                        context.EvaluatedPropertyNames.Add(match);
                        context.LocallyEvaluatedPropertyNames.Add(match);
                        var newContext = new SchemaValidationContext(context)
                        {
                            Instance             = obj[match],
                            BaseRelativeLocation = baseRelativeLocation,
                            RelativeLocation     = relativeLocation,
                            InstanceLocation     = context.InstanceLocation.CloneAndAppend(match),
                        };
                        var localResults = localSchema.Validate(newContext);
                        valid &= localResults.IsValid;
                        if (valid)
                        {
                            context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
                        }

                        if (JsonSchemaOptions.OutputFormat == SchemaValidationOutputFormat.Flag)
                        {
                            if (!valid)
                            {
                                Log.Schema("Subschema failed; halting validation early");
                                break;
                            }
                        }
                        else if (reportChildErrors)
                        {
                            nestedResults.Add(localResults);
                        }
                    }
                }
                else
                {
                    Log.Schema($"No properties found that match regular expression \"{pattern}\"");
                }
            }

            results.IsValid = valid;
            if (reportChildErrors)
            {
                results.NestedResults = nestedResults;
            }

            if (!results.IsValid)
            {
                results.ErrorMessage = ErrorTemplate;
            }

            return(results);
        }
Пример #13
0
        /// <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)
        {
            var results = new SchemaValidationResults(Name, context);

            if (context.Instance.Type != JsonValueType.Array)
            {
                Log.Schema("Instance not an array; not applicable");
                return(results);
            }

            var reportChildErrors = JsonSchemaOptions.ShouldReportChildErrors(this, context);
            var nestedResults     = new List <SchemaValidationResults>();
            var array             = context.Instance.Array;
            var failedIndices     = new JsonArray();

            if (IsArray)
            {
                // have array of schemata: validate in sequence
                Log.Schema("items is an array; process elements index-aligned");
                var i = 0;
                while (i < array.Count && i < Count)
                {
                    var newContext = new SchemaValidationContext(context)
                    {
                        Instance             = array[i],
                        BaseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name, i.ToString()),
                        RelativeLocation     = context.RelativeLocation.CloneAndAppend(Name, i.ToString()),
                        InstanceLocation     = context.InstanceLocation.CloneAndAppend(i.ToString()),
                    };
                    var localResults = this[i].Validate(newContext);
                    if (JsonSchemaOptions.OutputFormat == SchemaValidationOutputFormat.Flag && !localResults.IsValid)
                    {
                        Log.Schema("Subschema failed; halting validation early");
                        results.IsValid = false;
                        break;
                    }
                    if (!localResults.IsValid)
                    {
                        failedIndices.Add(i);
                    }
                    else
                    {
                        context.LocallyValidatedIndices.Add(i);
                    }
                    if (reportChildErrors)
                    {
                        nestedResults.Add(this[i].Validate(newContext));
                    }
                    context.LastEvaluatedIndex          = Math.Max(context.LastEvaluatedIndex, i);
                    context.LocalTierLastEvaluatedIndex = Math.Max(context.LocalTierLastEvaluatedIndex, i);
                    context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);
                    i++;
                }

                results.IsValid       = nestedResults.All(r => r.IsValid);
                results.NestedResults = nestedResults;
            }
            else
            {
                Log.Schema("items is an single subschema; process all elements");
                // have single schema: validate all against this
                var baseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name);
                var relativeLocation     = context.RelativeLocation.CloneAndAppend(Name);
                var valid = true;
                var i     = 0;

                foreach (var jv in array)
                {
                    var newContext = new SchemaValidationContext(context)
                    {
                        Instance             = jv,
                        BaseRelativeLocation = baseRelativeLocation,
                        RelativeLocation     = relativeLocation,
                        InstanceLocation     = context.InstanceLocation.CloneAndAppend(i.ToString()),
                    };
                    var localResults = this[0].Validate(newContext);
                    valid &= localResults.IsValid;
                    if (!localResults.IsValid)
                    {
                        failedIndices.Add(i);
                    }
                    else
                    {
                        context.LocallyValidatedIndices.Add(i);
                    }
                    context.LastEvaluatedIndex          = Math.Max(context.LastEvaluatedIndex, i);
                    context.LocalTierLastEvaluatedIndex = Math.Max(context.LocalTierLastEvaluatedIndex, i);
                    context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);

                    if (JsonSchemaOptions.OutputFormat == SchemaValidationOutputFormat.Flag)
                    {
                        if (!valid)
                        {
                            Log.Schema("Subschema failed; halting validation early");
                            break;
                        }
                    }
                    else if (reportChildErrors)
                    {
                        nestedResults.Add(localResults);
                    }

                    i++;
                }

                results.IsValid       = valid;
                results.NestedResults = nestedResults;
            }

            if (!results.IsValid)
            {
                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);
        }
        /// <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)
        {
            var baseRelativeLocation = context.BaseRelativeLocation?.CloneAndAppend(Name);
            var relativeLocation     = context.RelativeLocation.CloneAndAppend(Name);

            var valid             = true;
            var reportChildErrors = context.Options.ShouldReportChildErrors(this, context);
            var nestedResults     = new List <SchemaValidationResults>();
            var failedCount       = 0;

            foreach (var d in this)
            {
                var newContext = new SchemaValidationContext(context)
                {
                    BaseRelativeLocation = baseRelativeLocation,
                    RelativeLocation     = relativeLocation,
                    Misc = { ["dependencyParent"] = Name }
                };
                var localResults = d.Validate(newContext);
                valid &= localResults.IsValid;
                if (!localResults.IsValid)
                {
                    failedCount++;
                }
                else if (context.ShouldTrackValidatedValues)
                {
                    newContext.LocallyEvaluatedPropertyNames.Add(d.PropertyName);
                }
                context.UpdateEvaluatedPropertiesAndItemsFromSubschemaValidation(newContext);

                if (context.Options.OutputFormat == SchemaValidationOutputFormat.Flag)
                {
                    if (!valid)
                    {
                        Log.Schema(() => "Subschema failed; halting validation early");
                        break;
                    }
                }
                else if (reportChildErrors)
                {
                    nestedResults.Add(localResults);
                }
            }

            var results = new SchemaValidationResults(Name, context)
            {
                IsValid = valid
            };

            if (context.Options.OutputFormat == SchemaValidationOutputFormat.Flag)
            {
                results.NestedResults = nestedResults;
            }
            else if (!results.IsValid)
            {
                results.AdditionalInfo["failed"] = failedCount;
                results.AdditionalInfo["total"]  = Count;
                results.ErrorMessage             = ErrorTemplate.ResolveTokens(results.AdditionalInfo);
            }

            return(results);
        }