protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { if (depth == InitialDepth && JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { if (!GetChildren().All(IsValidPredicate)) { List <int> invalidIndexes = new List <int>(); int index = 0; foreach (SchemaScope schemaScope in GetChildren()) { if (!schemaScope.IsValid) { invalidIndexes.Add(index); } index++; } IFormattable message = $"JSON does not match all schemas from 'allOf'. Invalid schema indexes: {StringHelpers.Join(", ", invalidIndexes)}."; RaiseError(message, ErrorType.AllOf, ParentSchemaScope.Schema, null, ConditionalContext.Errors); } return(true); } return(false); }
protected void EnsureEnum(JsonToken token, object value) { if (Schema._enum != null && Schema._enum.Count > 0) { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(token, value); } } if (JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { if (!Schema._enum.ContainsValue(Context.TokenWriter.CurrentToken, JToken.EqualityComparer)) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); Context.TokenWriter.CurrentToken.WriteTo(new JsonTextWriter(sw)); RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, sw.ToString()), ErrorType.Enum, Schema, value, null); } } } }
protected void EnsureEnum(JsonToken token, object value) { bool isEnum = !Schema._enum.IsNullOrEmpty(); bool hasConst = Schema.Const != null; bool hasValidator = !Schema._validators.IsNullOrEmpty(); if (isEnum || hasConst || hasValidator) { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(token, value); } } if (JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { JToken currentToken = Context.TokenWriter.CurrentToken; if (isEnum) { bool defined = JsonTokenHelpers.Contains(Schema._enum, currentToken); if (!defined) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); currentToken.WriteTo(new JsonTextWriter(sw)); RaiseError($"Value {sw.ToString()} is not defined in enum.", ErrorType.Enum, Schema, value, null); } } if (hasConst) { bool defined = Schema.Const.DeepEquals(currentToken); if (!defined) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); currentToken.WriteTo(new JsonTextWriter(sw)); RaiseError($"Value {sw.ToString()} does not match const.", ErrorType.Const, Schema, value, null); } } if (hasValidator) { JsonValidatorContext context = new JsonValidatorContext(this, Schema); foreach (JsonValidator validator in Schema._validators) { validator.Validate(currentToken, context); } } } } }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { if (depth == InitialDepth && JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { if (GetChildrenAnyValid()) { RaiseError($"JSON is valid against schema from 'not'.", ErrorType.Not, ParentSchemaScope.Schema, null, ConditionalContext.Errors); } return(true); } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { if (depth == InitialDepth && JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { if (!GetChildren().Any(IsValidPredicate)) { RaiseError($"JSON does not match any schemas from 'anyOf'.", ErrorType.AnyOf, ParentSchemaScope.Schema, null, ConditionalContext.Errors); } return(true); } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { if (depth == InitialDepth && JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { int validCount = GetChildrenValidCount(); if (validCount != 1) { List <int> validIndexes = new List <int>(); int index = 0; foreach (SchemaScope schemaScope in ChildScopes) { if (schemaScope.IsValid) { validIndexes.Add(index); } index++; } IFormattable message; if (validIndexes.Count > 0) { message = $"JSON is valid against more than one schema from 'oneOf'. Valid schema indexes: {StringHelpers.Join(", ", validIndexes)}."; } else { message = $"JSON is valid against no schemas from 'oneOf'."; } RaiseError(message, ErrorType.OneOf, ParentSchemaScope.Schema, null, ConditionalContext.Errors); } return(true); } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { if (depth == InitialDepth && JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { int validCount = GetChildren().Count(IsValidPredicate); if (validCount != 1) { List <int> validIndexes = new List <int>(); int index = 0; foreach (SchemaScope schemaScope in GetChildren()) { if (schemaScope.IsValid) { validIndexes.Add(index); } index++; } string message = "JSON is valid against more than one schema from 'oneOf'. "; if (validIndexes.Count > 0) { message += "Valid schema indexes: {0}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", validIndexes)); } else { message += "No valid schemas."; } RaiseError(message, ErrorType.OneOf, ParentSchemaScope.Schema, null, ConditionalContext.Errors); } return(true); } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { int relativeDepth = depth - InitialDepth; if (relativeDepth == 0) { EnsureEnum(token, value); switch (token) { case JsonToken.StartObject: TestType(Schema, JSchemaType.Object); return(false); case JsonToken.EndObject: if (!Schema._required.IsNullOrEmpty() && _requiredProperties.Count > 0) { RaiseError($"Required properties are missing from object: {StringHelpers.Join(", ", _requiredProperties)}.", ErrorType.Required, Schema, _requiredProperties, null); } if (Schema.MaximumProperties != null && _propertyCount > Schema.MaximumProperties) { RaiseError($"Object property count {_propertyCount} exceeds maximum count of {Schema.MaximumProperties}.", ErrorType.MaximumProperties, Schema, _propertyCount, null); } if (Schema.MinimumProperties != null && _propertyCount < Schema.MinimumProperties) { RaiseError($"Object property count {_propertyCount} is less than minimum count of {Schema.MinimumProperties}.", ErrorType.MinimumProperties, Schema, _propertyCount, null); } if (!Schema._dependencies.IsNullOrEmpty()) { foreach (string readProperty in _readProperties) { object dependency; if (Schema._dependencies.TryGetValue(readProperty, out dependency)) { List <string> requiredProperties = dependency as List <string>; if (requiredProperties != null) { if (!requiredProperties.All(r => _readProperties.Contains(r))) { IEnumerable <string> missingRequiredProperties = requiredProperties.Where(r => !_readProperties.Contains(r)); IFormattable message = $"Dependencies for property '{readProperty}' failed. Missing required keys: {StringHelpers.Join(", ", missingRequiredProperties)}."; RaiseError(message, ErrorType.Dependencies, Schema, readProperty, null); } } else { SchemaScope dependencyScope = _dependencyScopes[readProperty]; if (dependencyScope.Context.HasErrors) { IFormattable message = $"Dependencies for property '{readProperty}' failed."; RaiseError(message, ErrorType.Dependencies, Schema, readProperty, ((ConditionalContext)dependencyScope.Context).Errors); } } } } } if (Schema._patternProperties != null) { foreach (PatternSchema patternSchema in Schema.GetPatternSchemas()) { Regex regex; string errorMessage; if (!patternSchema.TryGetPatternRegex(out regex, out errorMessage)) { RaiseError($"Could not test property names with regex pattern '{patternSchema.Pattern}'. There was an error parsing the regex: {errorMessage}", ErrorType.PatternProperties, Schema, patternSchema.Pattern, null); } } } return(true); default: throw new InvalidOperationException("Unexpected token when evaluating object: " + token); } } if (relativeDepth == 1) { if (token == JsonToken.PropertyName) { _propertyCount++; _currentPropertyName = (string)value; if (!Schema._required.IsNullOrEmpty()) { _requiredProperties.Remove(_currentPropertyName); } if (!Schema._dependencies.IsNullOrEmpty()) { _readProperties.Add(_currentPropertyName); } if (!Schema.AllowAdditionalProperties) { if (!IsPropertyDefinied(Schema, _currentPropertyName)) { IFormattable message = $"Property '{_currentPropertyName}' has not been defined and the schema does not allow additional properties."; RaiseError(message, ErrorType.AdditionalProperties, Schema, _currentPropertyName, null); } } } else { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { bool matched = false; if (Schema._properties != null) { JSchema propertySchema; if (Schema._properties.TryGetValue(_currentPropertyName, out propertySchema)) { CreateScopesAndEvaluateToken(token, value, depth, propertySchema); matched = true; } } if (Schema._patternProperties != null) { foreach (PatternSchema patternProperty in Schema.GetPatternSchemas()) { Regex regex; string errorMessage; if (patternProperty.TryGetPatternRegex(out regex, out errorMessage)) { if (regex.IsMatch(_currentPropertyName)) { CreateScopesAndEvaluateToken(token, value, depth, patternProperty.Schema); matched = true; } } } } if (!matched) { if (Schema.AllowAdditionalProperties && Schema.AdditionalProperties != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema.AdditionalProperties); } } } } } return(false); }
protected void EnsureEnum(JsonToken token, object?value) { bool isEnum = !Schema._enum.IsNullOrEmpty(); bool hasConst = Schema.Const != null; bool hasValidator = !Schema._validators.IsNullOrEmpty(); if (isEnum || hasConst || hasValidator) { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(token, value); } } else if (token == JsonToken.PropertyName) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(JsonToken.String, value); } } if (JsonTokenHelpers.IsPrimitiveOrEndToken(token) || token == JsonToken.PropertyName) { ValidationUtils.Assert(Context.TokenWriter != null); JToken currentToken = Context.TokenWriter.CurrentToken !; if (isEnum) { JToken resolvedToken = (currentToken is JProperty property) ? new JValue(property.Name) : currentToken; bool defined = JsonTokenHelpers.Contains(Schema._enum !, resolvedToken); if (!defined) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); currentToken.WriteTo(new JsonTextWriter(sw)); RaiseError($"Value {sw.ToString()} is not defined in enum.", ErrorType.Enum, Schema, value, null); } } if (hasConst) { bool defined = JsonTokenHelpers.ImplicitDeepEquals(Schema.Const !, currentToken); if (!defined) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); currentToken.WriteTo(new JsonTextWriter(sw)); RaiseError($"Value {sw.ToString()} does not match const.", ErrorType.Const, Schema, value, null); } } if (hasValidator) { JsonValidatorContext context = new JsonValidatorContext(this, Schema); foreach (JsonValidator validator in Schema._validators !) { validator.Validate(currentToken, context); } } } } }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { int relativeDepth = depth - InitialDepth; if (relativeDepth == 0) { EnsureEnum(token, value); switch (token) { case JsonToken.StartArray: EnsureValid(value); TestType(Schema, JSchemaType.Array); return(false); case JsonToken.StartConstructor: JSchemaType schemaType = Schema.Type.GetValueOrDefault(JSchemaType.None); if (schemaType != JSchemaType.None) { RaiseError($"Invalid type. Expected {schemaType.GetDisplayText()} but got Constructor.", ErrorType.Type, Schema, value, null); } return(false); case JsonToken.EndArray: case JsonToken.EndConstructor: ValidateConditionalChildren(token, value, depth); int itemCount = _index + 1; if (Schema.MaximumItems != null && itemCount > Schema.MaximumItems) { RaiseError($"Array item count {itemCount} exceeds maximum count of {Schema.MaximumItems}.", ErrorType.MaximumItems, Schema, itemCount, null); } if (Schema.MinimumItems != null && itemCount < Schema.MinimumItems) { RaiseError($"Array item count {itemCount} is less than minimum count of {Schema.MinimumItems}.", ErrorType.MinimumItems, Schema, itemCount, null); } if (Schema.Contains != null) { // MinimumContains overrides default contains behavior if (Schema.MinimumContains != null) { if (_matchCount < Schema.MinimumContains) { RaiseError($"Contains match count {_matchCount} is less than minimum contains count of {Schema.MinimumContains}.", ErrorType.MinimumContains, Schema, null, GetValidationErrors(_containsContexts)); } } else { if (_matchCount == 0) { RaiseError($"No items match contains.", ErrorType.Contains, Schema, null, GetValidationErrors(_containsContexts)); } } if (_matchCount > Schema.MaximumContains) { RaiseError($"Contains match count {_matchCount} exceeds maximum contains count of {Schema.MaximumContains}.", ErrorType.MaximumContains, Schema, null, GetValidationErrors(_containsContexts)); } } if (!_unevaluatedScopes.IsNullOrEmpty()) { foreach (KeyValuePair <int, UnevaluatedContext> item in _unevaluatedScopes) { if (!item.Value.Evaluated) { IFormattable message = $"Item at index {item.Key} has not been successfully evaluated and the schema does not allow unevaluated items."; RaiseError(message, ErrorType.UnevaluatedItems, Schema, item.Key, item.Value.SchemaScope.GetValidationErrors()); } } } return(true); default: throw new InvalidOperationException("Unexpected token when evaluating array: " + token); } } if (relativeDepth == 1) { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { _index++; if (Schema.UniqueItems || !Schema._validators.IsNullOrEmpty()) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(token, value); } } bool matched = false; if (Schema.ItemsPositionValidation) { // TODO: Remove LINQ JSchema itemSchema = Schema._items?.ElementAtOrDefault(_index); if (itemSchema != null) { CreateScopesAndEvaluateToken(token, value, depth, itemSchema); matched = true; } else { if (!Schema.AllowAdditionalItems) { RaiseError($"Index {_index + 1} has not been defined and the schema does not allow additional items.", ErrorType.AdditionalItems, Schema, value, null); } else if (Schema.AdditionalItems != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema.AdditionalItems); matched = true; } } } else { if (!Schema._items.IsNullOrEmpty()) { CreateScopesAndEvaluateToken(token, value, depth, Schema._items[0]); matched = true; } } if (ShouldEvaluateContains()) { ConditionalContext containsContext = CreateConditionalContext(); _containsContexts.Add(containsContext); // contains scope should not have the current scope the parent // do not want contain failures setting the current scope's IsValid CreateScopesAndEvaluateToken(token, value, depth, Schema.Contains, null, containsContext); } if (!matched) { if (ShouldValidateUnevaluated()) { _unevaluatedScopes[_index] = Schema.UnevaluatedItems != null ? new UnevaluatedContext(CreateScopesAndEvaluateToken(token, value, depth, Schema.UnevaluatedItems, this, CreateConditionalContext())) : new UnevaluatedContext(AlwaysFalseScope.Instance); } } } if (JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { if (Schema.UniqueItems) { JToken currentToken = Context.TokenWriter.CurrentToken; bool isDuplicate = JsonTokenHelpers.Contains(_uniqueArrayItems, currentToken); if (isDuplicate) { object v = (currentToken is JValue valueToken) ? valueToken.Value : currentToken; RaiseError($"Non-unique array item at index {_index}.", ErrorType.UniqueItems, Schema, v, null); } else { _uniqueArrayItems.Add(Context.TokenWriter.CurrentToken); } } if (ShouldEvaluateContains()) { ConditionalContext currentContainsContext = _containsContexts[_containsContexts.Count - 1]; if (!currentContainsContext.HasErrors) { _matchCount++; } } if (ShouldValidateUnevaluated() && _unevaluatedScopes.TryGetValue(_index, out UnevaluatedContext unevaluatedContext)) { // Property is valid against unevaluatedItems schema so no need to search further bool isValid = unevaluatedContext.SchemaScope.IsValid; unevaluatedContext.Evaluated = isValid; if (!isValid) { for (int i = Context.Scopes.Count - 1; i >= 0; i--) { Scope scope = Context.Scopes[i]; if (scope.InitialDepth == InitialDepth + 1) { // Schema for a item if (scope.Parent != null && scope is SchemaScope schemaScope && schemaScope.IsValid) { unevaluatedContext.AddValidScope(schemaScope.Parent.Schema); } } else if (scope.InitialDepth == InitialDepth) { // Schema for the current array. // Need to check these for oneOf, allOf, etc. if (scope is SchemaScope schemaScope) { if (schemaScope.Schema._allowAdditionalItems.GetValueOrDefault() || schemaScope.Schema.AdditionalItems != null || schemaScope.Schema.AllowUnevaluatedItems.GetValueOrDefault()) { unevaluatedContext.AddValidScope(schemaScope.Schema); } } } else if (scope.InitialDepth < InitialDepth) { break; } } } } } } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { int relativeDepth = depth - InitialDepth; if (relativeDepth == 0) { EnsureEnum(token, value); switch (token) { case JsonToken.StartArray: EnsureValid(value); TestType(Schema, JSchemaType.Array); return(false); case JsonToken.StartConstructor: JSchemaType schemaType = Schema.Type.GetValueOrDefault(JSchemaType.None); if (schemaType != JSchemaType.None) { RaiseError($"Invalid type. Expected {schemaType.GetDisplayText()} but got Constructor.", ErrorType.Type, Schema, value, null); } return(false); case JsonToken.EndArray: case JsonToken.EndConstructor: ValidateConditionalChildren(token, value, depth); int itemCount = _index + 1; if (Schema.MaximumItems != null && itemCount > Schema.MaximumItems) { RaiseError($"Array item count {itemCount} exceeds maximum count of {Schema.MaximumItems}.", ErrorType.MaximumItems, Schema, itemCount, null); } if (Schema.MinimumItems != null && itemCount < Schema.MinimumItems) { RaiseError($"Array item count {itemCount} is less than minimum count of {Schema.MinimumItems}.", ErrorType.MinimumItems, Schema, itemCount, null); } if (Schema.Contains != null && !_matchContains) { List <ValidationError> containsErrors = new List <ValidationError>(); foreach (ConditionalContext containsContext in _containsContexts) { containsErrors.AddRange(containsContext.Errors); } RaiseError($"No items match contains.", ErrorType.Contains, Schema, null, containsErrors); } return(true); default: throw new InvalidOperationException("Unexpected token when evaluating array: " + token); } } if (relativeDepth == 1) { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { _index++; if (Schema.UniqueItems || !Schema._validators.IsNullOrEmpty()) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(token, value); } } if (Schema.ItemsPositionValidation) { JSchema itemSchema = Schema._items?.ElementAtOrDefault(_index); if (itemSchema != null) { CreateScopesAndEvaluateToken(token, value, depth, itemSchema); } else { if (!Schema.AllowAdditionalItems) { RaiseError($"Index {_index + 1} has not been defined and the schema does not allow additional items.", ErrorType.AdditionalItems, Schema, value, null); } else if (Schema.AdditionalItems != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema.AdditionalItems); } } } else { if (!Schema._items.IsNullOrEmpty()) { CreateScopesAndEvaluateToken(token, value, depth, Schema._items[0]); } } // no longer need to check contains schema after match if (Schema.Contains != null && !_matchContains) { ConditionalContext containsContext = ConditionalContext.Create(Context); _containsContexts.Add(containsContext); // contains scope should not have the current scope the parent // do not want contain failures setting the current scope's IsValid CreateScopesAndEvaluateToken(token, value, depth, Schema.Contains, null, containsContext); } } if (JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { if (Schema.UniqueItems) { JToken currentToken = Context.TokenWriter.CurrentToken; bool isDuplicate = JsonTokenHelpers.Contains(_uniqueArrayItems, currentToken); if (isDuplicate) { object v = (currentToken is JValue valueToken) ? valueToken.Value : currentToken; RaiseError($"Non-unique array item at index {_index}.", ErrorType.UniqueItems, Schema, v, null); } else { _uniqueArrayItems.Add(Context.TokenWriter.CurrentToken); } } if (Schema.Contains != null && !_matchContains) { ConditionalContext currentContainsContext = _containsContexts[_containsContexts.Count - 1]; if (!currentContainsContext.HasErrors) { _matchContains = true; // no longer need previous errors after match _containsContexts.Clear(); } } } } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object?value, int depth) { int relativeDepth = depth - InitialDepth; if (relativeDepth == 0) { EnsureEnum(token, value); switch (token) { case JsonToken.StartObject: EnsureValid(value); TestType(Schema, JSchemaType.Object); return(false); case JsonToken.EndObject: ValidateConditionalChildren(token, value, depth); if (!Schema._required.IsNullOrEmpty() && _requiredProperties !.Count > 0) { //capture required properties at current depth as we may clear _requiredProperties later on List <string> capturedRequiredProperties = _requiredProperties.ToList(); RaiseError($"Required properties are missing from object: {StringHelpers.Join(", ", capturedRequiredProperties)}.", ErrorType.Required, Schema, capturedRequiredProperties, null); } if (Schema.MaximumProperties != null && _propertyCount > Schema.MaximumProperties) { RaiseError($"Object property count {_propertyCount} exceeds maximum count of {Schema.MaximumProperties}.", ErrorType.MaximumProperties, Schema, _propertyCount, null); } if (Schema.MinimumProperties != null && _propertyCount < Schema.MinimumProperties) { RaiseError($"Object property count {_propertyCount} is less than minimum count of {Schema.MinimumProperties}.", ErrorType.MinimumProperties, Schema, _propertyCount, null); } if (HasDependencies()) { foreach (string readProperty in _readProperties !) { object? dependency = null; IList <string>?requiredProperties = null; if (Schema._dependencies?.TryGetValue(readProperty, out dependency) ?? false) { requiredProperties = dependency as IList <string>; if (requiredProperties != null) { ValidateDependantProperties(readProperty, requiredProperties); } else { ValidateDependantSchema(readProperty); } } if (Schema._dependentRequired?.TryGetValue(readProperty, out requiredProperties) ?? false) { ValidateDependantProperties(readProperty, requiredProperties !); } if (Schema._dependentSchemas?.TryGetValue(readProperty, out _) ?? false) { ValidateDependantSchema(readProperty); } } } // Evaluate after dependency schemas have been validated if (!_unevaluatedScopes.IsNullOrEmpty()) { foreach (KeyValuePair <string, UnevaluatedContext> item in _unevaluatedScopes) { if (!item.Value.Evaluated) { IFormattable message = $"Property '{item.Key}' has not been successfully evaluated and the schema does not allow unevaluated properties."; RaiseError(message, ErrorType.UnevaluatedProperties, Schema, item.Key, item.Value.SchemaScope.GetValidationErrors()); } } } if (Schema._patternProperties != null) { foreach (PatternSchema patternSchema in Schema.GetPatternSchemas()) { if (!patternSchema.TryGetPatternRegex( #if !(NET35 || NET40) Context.Validator.RegexMatchTimeout, #endif out Regex? _, out string?errorMessage)) { RaiseError($"Could not test property names with regex pattern '{patternSchema.Pattern}'. There was an error parsing the regex: {errorMessage}", ErrorType.PatternProperties, Schema, patternSchema.Pattern, null); } } } return(true); default: throw new InvalidOperationException("Unexpected token when evaluating object: " + token); } } if (relativeDepth == 1) { if (token == JsonToken.PropertyName) { _propertyCount++; _currentPropertyName = (string)value !; if (!Schema._required.IsNullOrEmpty()) { _requiredProperties !.Remove(_currentPropertyName); } if (HasDependencies()) { _readProperties !.Add(_currentPropertyName); } if (Schema._propertyNames != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema._propertyNames); } if (!Schema.AllowAdditionalProperties) { if (!IsPropertyDefined(Schema, _currentPropertyName)) { IFormattable message = $"Property '{_currentPropertyName}' has not been defined and the schema does not allow additional properties."; RaiseError(message, ErrorType.AdditionalProperties, Schema, _currentPropertyName, null); } } } else { ValidationUtils.Assert(_currentPropertyName != null); if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { bool matched = false; if (Schema._properties != null) { if (Schema._properties.TryGetValue(_currentPropertyName, out JSchema propertySchema)) { CreateScopesAndEvaluateToken(token, value, depth, propertySchema); matched = true; } } if (Schema._patternProperties != null) { foreach (PatternSchema patternProperty in Schema.GetPatternSchemas()) { if (patternProperty.TryGetPatternRegex( #if !(NET35 || NET40) Context.Validator.RegexMatchTimeout, #endif out Regex? regex, out string?_)) { if (RegexHelpers.IsMatch(regex, patternProperty.Pattern, _currentPropertyName)) { CreateScopesAndEvaluateToken(token, value, depth, patternProperty.Schema); matched = true; } } } } if (!matched) { if (Schema.AllowAdditionalProperties && Schema.AdditionalProperties != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema.AdditionalProperties); } if (ShouldValidateUnevaluated()) { _unevaluatedScopes ![_currentPropertyName] = Schema.UnevaluatedProperties != null
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { int relativeDepth = depth - InitialDepth; if (relativeDepth == 0) { EnsureEnum(token, value); switch (token) { case JsonToken.StartArray: TestType(Schema, JSchemaType.Array, null); return(false); case JsonToken.StartConstructor: RaiseError($"Invalid type. Expected {Schema.Type} but got Constructor.", ErrorType.Type, Schema, value, null); return(false); case JsonToken.EndArray: case JsonToken.EndConstructor: int itemCount = _index + 1; if (Schema.MaximumItems != null && itemCount > Schema.MaximumItems) { RaiseError($"Array item count {itemCount} exceeds maximum count of {Schema.MaximumItems}.", ErrorType.MaximumItems, Schema, itemCount, null); } if (Schema.MinimumItems != null && itemCount < Schema.MinimumItems) { RaiseError($"Array item count {itemCount} is less than minimum count of {Schema.MinimumItems}.", ErrorType.MinimumItems, Schema, itemCount, null); } return(true); default: throw new InvalidOperationException("Unexpected token when evaluating array: " + token); } } if (relativeDepth == 1) { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { _index++; if (Schema.UniqueItems) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(token, value); } } if (Schema.ItemsPositionValidation) { JSchema itemSchema = (Schema._items != null) ? Schema._items.ElementAtOrDefault(_index) : null; if (itemSchema != null) { CreateScopesAndEvaluateToken(token, value, depth, itemSchema); } else { if (!Schema.AllowAdditionalItems) { RaiseError($"Index {_index + 1} has not been defined and the schema does not allow additional items.", ErrorType.AdditionalItems, Schema, value, null); } else if (Schema.AdditionalItems != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema.AdditionalItems); } } } else { if (Schema._items != null && Schema._items.Count > 0) { CreateScopesAndEvaluateToken(token, value, depth, Schema._items[0]); } } } if (JsonTokenHelpers.IsPrimitiveOrEndToken(token)) { if (Schema.UniqueItems) { var currentToken = Context.TokenWriter.CurrentToken; if (_uniqueArrayItems.Contains(currentToken, JToken.EqualityComparer)) { object v = (currentToken is JValue) ? ((JValue)currentToken).Value : currentToken; RaiseError($"Non-unique array item at index {_index}.", ErrorType.UniqueItems, Schema, v, null); } else { _uniqueArrayItems.Add(Context.TokenWriter.CurrentToken); } } } } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object value, int depth) { int relativeDepth = depth - InitialDepth; if (relativeDepth == 0) { EnsureEnum(token, value); switch (token) { case JsonToken.StartObject: TestType(Schema, JSchemaType.Object, null); return(false); case JsonToken.EndObject: if (_requiredProperties != null && _requiredProperties.Count > 0) { RaiseError("Required properties are missing from object: {0}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", _requiredProperties)), ErrorType.Required, Schema, _requiredProperties, null); } if (Schema.MaximumProperties != null && _propertyCount > Schema.MaximumProperties) { RaiseError("Object property count {0} exceeds maximum count of {1}.".FormatWith(CultureInfo.InvariantCulture, _propertyCount, Schema.MaximumProperties), ErrorType.MaximumProperties, Schema, _propertyCount, null); } if (Schema.MinimumProperties != null && _propertyCount < Schema.MinimumProperties) { RaiseError("Object property count {0} is less than minimum count of {1}.".FormatWith(CultureInfo.InvariantCulture, _propertyCount, Schema.MinimumProperties), ErrorType.MinimumProperties, Schema, _propertyCount, null); } if (_readProperties != null) { foreach (string readProperty in _readProperties) { object dependency; if (Schema._dependencies.TryGetValue(readProperty, out dependency)) { List <string> requiredProperties = dependency as List <string>; if (requiredProperties != null) { if (!requiredProperties.All(r => _readProperties.Contains(r))) { List <string> missingRequiredProperties = requiredProperties.Where(r => !_readProperties.Contains(r)).ToList(); string message = "Dependencies for property '{0}' failed. Missing required keys: {1}.".FormatWith(CultureInfo.InvariantCulture, readProperty, string.Join(", ", missingRequiredProperties)); RaiseError(message, ErrorType.Dependencies, Schema, readProperty, null); } } else { SchemaScope dependencyScope = _dependencyScopes[readProperty]; if (dependencyScope.Context.HasErrors) { string message = "Dependencies for property '{0}' failed.".FormatWith(CultureInfo.InvariantCulture, readProperty); RaiseError(message, ErrorType.Dependencies, Schema, readProperty, ((ConditionalContext)dependencyScope.Context).Errors); } } } } } return(true); default: throw new InvalidOperationException("Unexpected token when evaluating object: " + token); } } if (relativeDepth == 1) { if (token == JsonToken.PropertyName) { _propertyCount++; _currentPropertyName = (string)value; if (_requiredProperties != null) { _requiredProperties.Remove(_currentPropertyName); } if (_readProperties != null) { _readProperties.Add(_currentPropertyName); } if (!Schema.AllowAdditionalProperties) { if (!IsPropertyDefinied(Schema, _currentPropertyName)) { string message = "Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, _currentPropertyName); RaiseError(message, ErrorType.AdditionalProperties, Schema, _currentPropertyName, null); } } } else { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { bool matched = false; if (Schema._properties != null) { JSchema propertySchema; if (Schema._properties.TryGetValue(_currentPropertyName, out propertySchema)) { CreateScopesAndEvaluateToken(token, value, depth, propertySchema); matched = true; } } if (Schema._patternProperties != null) { foreach (KeyValuePair <string, JSchema> patternProperty in Schema._patternProperties) { if (Regex.IsMatch(_currentPropertyName, patternProperty.Key)) { CreateScopesAndEvaluateToken(token, value, depth, patternProperty.Value); matched = true; } } } if (!matched) { if (Schema.AllowAdditionalProperties && Schema.AdditionalProperties != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema.AdditionalProperties); } } } } } return(false); }
protected override bool EvaluateTokenCore(JsonToken token, object?value, int depth) { int relativeDepth = depth - InitialDepth; if (relativeDepth == 0) { EnsureEnum(token, value); switch (token) { case JsonToken.StartArray: EnsureValid(value); TestType(Schema, JSchemaType.Array); return(false); case JsonToken.StartConstructor: JSchemaType schemaType = Schema.Type.GetValueOrDefault(JSchemaType.None); if (schemaType != JSchemaType.None) { RaiseError($"Invalid type. Expected {schemaType.GetDisplayText()} but got Constructor.", ErrorType.Type, Schema, value, null); } return(false); case JsonToken.EndArray: case JsonToken.EndConstructor: ValidateConditionalChildren(token, value, depth); int itemCount = _index + 1; if (Schema.MaximumItems != null && itemCount > Schema.MaximumItems) { RaiseError($"Array item count {itemCount} exceeds maximum count of {Schema.MaximumItems}.", ErrorType.MaximumItems, Schema, itemCount, null); } if (Schema.MinimumItems != null && itemCount < Schema.MinimumItems) { RaiseError($"Array item count {itemCount} is less than minimum count of {Schema.MinimumItems}.", ErrorType.MinimumItems, Schema, itemCount, null); } if (Schema.Contains != null) { ValidationUtils.Assert(_containsContexts != null); // MinimumContains overrides default contains behavior if (Schema.MinimumContains != null) { if (_matchCount < Schema.MinimumContains) { RaiseError($"Contains match count {_matchCount} is less than minimum contains count of {Schema.MinimumContains}.", ErrorType.MinimumContains, Schema, null, GetValidationErrors(_containsContexts)); } } else { if (_matchCount == 0) { RaiseError($"No items match contains.", ErrorType.Contains, Schema, null, GetValidationErrors(_containsContexts)); } } if (_matchCount > Schema.MaximumContains) { RaiseError($"Contains match count {_matchCount} exceeds maximum contains count of {Schema.MaximumContains}.", ErrorType.MaximumContains, Schema, null, GetValidationErrors(_containsContexts)); } } if (!_unevaluatedScopes.IsNullOrEmpty()) { foreach (KeyValuePair <int, UnevaluatedContext> item in _unevaluatedScopes) { if (!item.Value.Evaluated) { IFormattable message = $"Item at index {item.Key} has not been successfully evaluated and the schema does not allow unevaluated items."; RaiseError(message, ErrorType.UnevaluatedItems, Schema, item.Key, item.Value.SchemaScope.GetValidationErrors()); } } } return(true); default: throw new InvalidOperationException("Unexpected token when evaluating array: " + token); } } if (relativeDepth == 1) { if (JsonTokenHelpers.IsPrimitiveOrStartToken(token)) { _index++; if (Schema.UniqueItems || !Schema._validators.IsNullOrEmpty()) { if (Context.TokenWriter == null) { Context.TokenWriter = new JTokenWriter(); Context.TokenWriter.WriteToken(token, value); } } bool matched = false; if (Schema.ItemsPositionValidation) { // TODO: Remove LINQ JSchema?itemSchema = Schema._items?.ElementAtOrDefault(_index); if (itemSchema != null) { CreateScopesAndEvaluateToken(token, value, depth, itemSchema); matched = true; } else { if (!Schema.AllowAdditionalItems) { RaiseError($"Index {_index + 1} has not been defined and the schema does not allow additional items.", ErrorType.AdditionalItems, Schema, value, null); } else if (Schema.AdditionalItems != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema.AdditionalItems); matched = true; } } } else { if (!Schema._items.IsNullOrEmpty()) { CreateScopesAndEvaluateToken(token, value, depth, Schema._items[0]); matched = true; } } if (ShouldEvaluateContains()) { ConditionalContext containsContext = CreateConditionalContext(); _containsContexts.Add(containsContext); // contains scope should not have the current scope the parent // do not want contain failures setting the current scope's IsValid CreateScopesAndEvaluateToken(token, value, depth, Schema.Contains !, null, containsContext); } if (!matched) { if (ShouldValidateUnevaluated()) { _unevaluatedScopes ![_index] = Schema.UnevaluatedItems != null