private bool IsPropertyDefinied(JSchema schema, string propertyName) { if (schema._properties != null && schema._properties.ContainsKey(propertyName)) { return(true); } if (schema._patternProperties != null) { foreach (PatternSchema patternSchema in schema.GetPatternSchemas()) { if (patternSchema.TryGetPatternRegex( #if !(NET35 || NET40) Context.Validator.RegexMatchTimeout, #endif out Regex regex, out string _)) { if (RegexHelpers.IsMatch(regex, patternSchema.Pattern, propertyName)) { return(true); } } } } return(false); }
internal static bool ValidateString(SchemaScope scope, JSchema schema, string value) { if (!TestType(scope, schema, JSchemaType.String, value)) { return(false); } if (schema.MaximumLength != null || schema.MinimumLength != null) { // want to test the character length and ignore unicode surrogates StringInfo stringInfo = new StringInfo(value); int textLength = stringInfo.LengthInTextElements; if (schema.MaximumLength != null && textLength > schema.MaximumLength) { scope.RaiseError($"String '{value}' exceeds maximum length of {schema.MaximumLength}.", ErrorType.MaximumLength, schema, value, null); } if (schema.MinimumLength != null && textLength < schema.MinimumLength) { scope.RaiseError($"String '{value}' is less than minimum length of {schema.MinimumLength}.", ErrorType.MinimumLength, schema, value, null); } } if (schema.Pattern != null) { if (schema.TryGetPatternRegex( #if !(NET35 || NET40) scope.Context.Validator.RegexMatchTimeout, #endif out Regex regex, out string errorMessage)) { if (!RegexHelpers.IsMatch(regex, schema.Pattern, value)) { scope.RaiseError($"String '{value}' does not match regex pattern '{schema.Pattern}'.", ErrorType.Pattern, schema, value, null); } } else { scope.RaiseError($"Could not validate string with regex pattern '{schema.Pattern}'. There was an error parsing the regex: {errorMessage}", ErrorType.Pattern, schema, value, 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.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 (!Schema._dependencies.IsNullOrEmpty()) { foreach (string readProperty in _readProperties) { if (Schema._dependencies.TryGetValue(readProperty, out object dependency)) { if (dependency is List <string> requiredProperties) { 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()) { 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 (!Schema._dependencies.IsNullOrEmpty()) { _readProperties.Add(_currentPropertyName); } if (Schema._propertyNames != null) { CreateScopesAndEvaluateToken(token, value, depth, Schema._propertyNames); } 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) { 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); } } } } } 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