private IEnumerable<string> GetRequiredProperties(JsonSchemaModel schema) { if (schema == null || schema.Properties == null) return Enumerable.Empty<string>(); return schema.Properties.Where(p => p.Value.Required).Select(p => p.Key); }
public static JsonSchemaModel Create(IList<JsonSchema> schemata) { JsonSchemaModel model = new JsonSchemaModel(); foreach (JsonSchema schema in schemata) { Combine(model, schema); } return model; }
private static void Combine(JsonSchemaModel model, JsonSchema schema) { // Version 3 of the Draft JSON Schema has the default value of Not Required model.Required = model.Required || (schema.Required ?? false); model.Type = model.Type & (schema.Type ?? JsonSchemaType.Any); model.MinimumLength = MathUtils.Max(model.MinimumLength, schema.MinimumLength); model.MaximumLength = MathUtils.Min(model.MaximumLength, schema.MaximumLength); // not sure what is the best way to combine divisibleBy model.DivisibleBy = MathUtils.Max(model.DivisibleBy, schema.DivisibleBy); model.Minimum = MathUtils.Max(model.Minimum, schema.Minimum); model.Maximum = MathUtils.Max(model.Maximum, schema.Maximum); model.ExclusiveMinimum = model.ExclusiveMinimum || (schema.ExclusiveMinimum ?? false); model.ExclusiveMaximum = model.ExclusiveMaximum || (schema.ExclusiveMaximum ?? false); model.MinimumItems = MathUtils.Max(model.MinimumItems, schema.MinimumItems); model.MaximumItems = MathUtils.Min(model.MaximumItems, schema.MaximumItems); model.AllowAdditionalProperties = model.AllowAdditionalProperties && schema.AllowAdditionalProperties; if (schema.Enum != null) { if (model.Enum == null) model.Enum = new List<JToken>(); model.Enum.AddRangeDistinct(schema.Enum, new JTokenEqualityComparer()); } model.Disallow = model.Disallow | (schema.Disallow ?? JsonSchemaType.None); if (schema.Pattern != null) { if (model.Patterns == null) model.Patterns = new List<string>(); model.Patterns.AddDistinct(schema.Pattern); } }
private bool ValidateObject(JsonSchemaModel schema) { if (schema == null) return true; return (TestType(schema, JsonSchemaType.Object)); }
private bool TestType(JsonSchemaModel currentSchema, JsonSchemaType currentType) { if (!JsonSchemaGenerator.HasFlag(currentSchema.Type, currentType)) { RaiseError("Invalid type. Expected {0} but got {1}.".FormatWith(CultureInfo.InvariantCulture, currentSchema.Type, currentType), currentSchema); return false; } return true; }
private bool IsPropertyDefinied(JsonSchemaModel schema, string propertyName) { if (schema.Properties != null && schema.Properties.ContainsKey(propertyName)) return true; if (schema.PatternProperties != null) { foreach (string pattern in schema.PatternProperties.Keys) { if (Regex.IsMatch(propertyName, pattern)) return true; } } return false; }
private bool ValidateArray(JsonSchemaModel schema) { if (schema == null) return true; return (TestType(schema, JsonSchemaType.Array)); }
private void ValidateFloat(JsonSchemaModel schema) { if (schema == null) return; if (!TestType(schema, JsonSchemaType.Float)) return; ValidateInEnumAndNotDisallowed(schema); double value = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture); if (schema.Maximum != null) { if (value > schema.Maximum) RaiseError("Float {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema); if (schema.ExclusiveMaximum && value == schema.Maximum) RaiseError("Float {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema); } if (schema.Minimum != null) { if (value < schema.Minimum) RaiseError("Float {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema); if (schema.ExclusiveMinimum && value == schema.Minimum) RaiseError("Float {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema); } if (schema.DivisibleBy != null && !IsZero(value % schema.DivisibleBy.Value)) RaiseError("Float {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema); }
private void ValidatePropertyName(JsonSchemaModel schema) { if (schema == null) return; string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); if (_currentScope.RequiredProperties.ContainsKey(propertyName)) _currentScope.RequiredProperties[propertyName] = true; if (!schema.AllowAdditionalProperties) { bool propertyDefinied = IsPropertyDefinied(schema, propertyName); if (!propertyDefinied) RaiseError("Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema); } _currentScope.CurrentPropertyName = propertyName; }
private void ValidateBoolean(JsonSchemaModel schema) { if (schema == null) return; if (!TestType(schema, JsonSchemaType.Boolean)) return; ValidateInEnumAndNotDisallowed(schema); }
private void ValidateString(JsonSchemaModel schema) { if (schema == null) return; if (!TestType(schema, JsonSchemaType.String)) return; ValidateInEnumAndNotDisallowed(schema); string value = _reader.Value.ToString(); if (schema.MaximumLength != null && value.Length > schema.MaximumLength) RaiseError("String '{0}' exceeds maximum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MaximumLength), schema); if (schema.MinimumLength != null && value.Length < schema.MinimumLength) RaiseError("String '{0}' is less than minimum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MinimumLength), schema); if (schema.Patterns != null) { foreach (string pattern in schema.Patterns) { if (!Regex.IsMatch(value, pattern)) RaiseError("String '{0}' does not match regex pattern '{1}'.".FormatWith(CultureInfo.InvariantCulture, value, pattern), schema); } } }
private void ValidateEndArray(JsonSchemaModel schema) { if (schema == null) return; int arrayItemCount = _currentScope.ArrayItemCount; if (schema.MaximumItems != null && arrayItemCount > schema.MaximumItems) RaiseError("Array item count {0} exceeds maximum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MaximumItems), schema); if (schema.MinimumItems != null && arrayItemCount < schema.MinimumItems) RaiseError("Array item count {0} is less than minimum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MinimumItems), schema); }
private void ValidateEndObject(JsonSchemaModel schema) { if (schema == null) return; Dictionary<string, bool> requiredProperties = _currentScope.RequiredProperties; if (requiredProperties != null) { List<string> unmatchedRequiredProperties = requiredProperties.Where(kv => !kv.Value).Select(kv => kv.Key).ToList(); if (unmatchedRequiredProperties.Count > 0) RaiseError("Required properties are missing from object: {0}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", unmatchedRequiredProperties.ToArray())), schema); } }
private void ValidateCurrentToken() { // first time validate has been called. build model if (_model == null) { JsonSchemaModelBuilder builder = new JsonSchemaModelBuilder(); _model = builder.Build(_schema); } switch (_reader.TokenType) { case JsonToken.StartObject: ProcessValue(); IList<JsonSchemaModel> objectSchemas = CurrentMemberSchemas.Where(ValidateObject).ToList(); Push(new SchemaScope(JTokenType.Object, objectSchemas)); break; case JsonToken.StartArray: ProcessValue(); IList<JsonSchemaModel> arraySchemas = CurrentMemberSchemas.Where(ValidateArray).ToList(); Push(new SchemaScope(JTokenType.Array, arraySchemas)); break; case JsonToken.StartConstructor: Push(new SchemaScope(JTokenType.Constructor, null)); break; case JsonToken.PropertyName: foreach (JsonSchemaModel schema in CurrentSchemas) { ValidatePropertyName(schema); } break; case JsonToken.Raw: break; case JsonToken.Integer: ProcessValue(); foreach (JsonSchemaModel schema in CurrentMemberSchemas) { ValidateInteger(schema); } break; case JsonToken.Float: ProcessValue(); foreach (JsonSchemaModel schema in CurrentMemberSchemas) { ValidateFloat(schema); } break; case JsonToken.String: ProcessValue(); foreach (JsonSchemaModel schema in CurrentMemberSchemas) { ValidateString(schema); } break; case JsonToken.Boolean: ProcessValue(); foreach (JsonSchemaModel schema in CurrentMemberSchemas) { ValidateBoolean(schema); } break; case JsonToken.Null: ProcessValue(); foreach (JsonSchemaModel schema in CurrentMemberSchemas) { ValidateNull(schema); } break; case JsonToken.Undefined: break; case JsonToken.EndObject: foreach (JsonSchemaModel schema in CurrentSchemas) { ValidateEndObject(schema); } Pop(); break; case JsonToken.EndArray: foreach (JsonSchemaModel schema in CurrentSchemas) { ValidateEndArray(schema); } Pop(); break; case JsonToken.EndConstructor: Pop(); break; case JsonToken.Date: case JsonToken.Bytes: // these have no equivalent in JSON schema break; case JsonToken.None: // no content, do nothing break; default: throw new ArgumentOutOfRangeException(); } }
private void ValidateInEnumAndNotDisallowed(JsonSchemaModel schema) { if (schema == null) return; JToken value = new JValue(_reader.Value); if (schema.Enum != null) { StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); value.WriteTo(new JsonTextWriter(sw)); if (!schema.Enum.ContainsValue(value, new JTokenEqualityComparer())) RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, sw.ToString()), schema); } JsonSchemaType? currentNodeType = GetCurrentNodeSchemaType(); if (currentNodeType != null) { if (JsonSchemaGenerator.HasFlag(schema.Disallow, currentNodeType.Value)) RaiseError("Type {0} is disallowed.".FormatWith(CultureInfo.InvariantCulture, currentNodeType), schema); } }
private void RaiseError(string message, JsonSchemaModel schema) { IJsonLineInfo lineInfo = this; string exceptionMessage = (lineInfo.HasLineInfo()) ? message + " Line {0}, position {1}.".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition) : message; OnValidationEvent(new JsonSchemaException(exceptionMessage, null, Path, lineInfo.LineNumber, lineInfo.LinePosition)); }