ConstraintsViolationException ValidateNumber(double v, State state, JsonSchemaRegistory reg) { if (_schema.MultipleOf != double.MinValue) { if (_schema.MultipleOf <= 0) { throw new InvalidOperationException("MultipleOf must be greater than 0: Value = " + _schema.MultipleOf); } var b = v / _schema.MultipleOf; if (b != Math.Truncate(b)) { var msg = state.CreateMessage("MultipleOf assertion !({0} % {1} == 0)", v, _schema.MultipleOf); return(new ConstraintsViolationException(msg)); } } if (_schema.Maximum != double.MinValue) { if (!(v <= _schema.Maximum)) { var msg = state.CreateMessage("Maximum assertion !({0} <= {1})", v, _schema.Maximum); return(new ConstraintsViolationException(msg)); } } if (_schema.ExclusiveMaximum != double.MinValue) { if (!(v < _schema.ExclusiveMaximum)) { var msg = state.CreateMessage("ExclusiveMaximum assertion !({0} < {1})", v, _schema.ExclusiveMaximum); return(new ConstraintsViolationException(msg)); } } if (_schema.Minimum != double.MaxValue) { if (!(v >= _schema.Minimum)) { var msg = state.CreateMessage("Minimum assertion !({0} >= {1})", v, _schema.Minimum); return(new ConstraintsViolationException(msg)); } } if (_schema.ExclusiveMinimum != double.MaxValue) { if (!(v > _schema.ExclusiveMinimum)) { var msg = state.CreateMessage("ExclusiveMinimum assertion !({0} > {1})", v, _schema.ExclusiveMinimum); return(new ConstraintsViolationException(msg)); } } return(null); }
internal static ConstraintsViolationException Validate(this JsonSchemaAttribute j, object o, Internal.State state, JsonSchemaRegistory reg) { return((new JsonSchemaValidator(j)).Validate(o, state, reg)); }
ConstraintsViolationException ValidateString(string v, State state, JsonSchemaRegistory reg) { StringInfo si = null; if (_schema.MaxLength != int.MinValue) { si = si ?? new StringInfo(v); if (!(si.LengthInTextElements <= _schema.MaxLength)) { var msg = state.CreateMessage("MaxLength assertion !({0} <= {1})", si.LengthInTextElements, _schema.MaxLength); return(new ConstraintsViolationException(msg)); } } if (_schema.MinLength != int.MaxValue) { si = si ?? new StringInfo(v); if (!(si.LengthInTextElements >= _schema.MinLength)) { var msg = state.CreateMessage("MinLength assertion !({0} >= {1})", si.LengthInTextElements, _schema.MinLength); return(new ConstraintsViolationException(msg)); } } if (_schema.Pattern != null) { if (!Regex.IsMatch(v, _schema.Pattern)) { var msg = state.CreateMessage("Pattern assertion !(\"{0}\" matched \"{1}\")", v, _schema.Pattern); return(new ConstraintsViolationException(msg)); } } return(null); }
ConstraintsViolationException ValidateObjectField(string key, object value, State state, JsonSchemaRegistory reg) { var matched = false; if (_schema._dynamicResolverTag != null) { Type dynElemType; if (DynamicResolver.Find(_schema._dynamicResolverTag, key, out dynElemType)) { var dynElemSchema = JsonSchemaAttribute.CreateFromType(dynElemType, reg, true); var ex = dynElemSchema.Validate(value, state, reg); if (ex != null) { return(new ConstraintsViolationException("DynamicResolver", ex)); } } } if (_schema.Properties != null) { JsonSchemaAttribute itemSchema = null; if (_schema.Properties.TryGetValue(key, out itemSchema)) { matched = true; var ex = itemSchema.Validate(value, state, reg); if (ex != null) { return(new ConstraintsViolationException("Property", ex)); } } } if (_schema.PatternProperties != null) { foreach (var pprop in _schema.PatternProperties) { if (Regex.IsMatch(key, pprop.Key)) { matched = true; var ex = pprop.Value.Validate(value, state, reg); if (ex != null) { return(new ConstraintsViolationException("PatternProperties", ex)); } } } } if (_schema.AdditionalProperties != null && !matched) { var ex = _schema.AdditionalProperties.Validate(value, state, reg); if (ex != null) { return(new ConstraintsViolationException("AdditionalProperties", ex)); } } return(null); }
ConstraintsViolationException ValidateObject(object v, State state, JsonSchemaRegistory reg) { var validated = new Dictionary <string, object>(); foreach (var kv in TypeHelper.ToKeyValues(v)) { var ex = ValidateObjectField(kv.Key, kv.Value, state.NestAsElem(kv.Key), reg); if (ex != null) { return(ex); } validated.Add(kv.Key, kv.Value); } if (_schema.Required != null) { var req = new HashSet <string>(_schema.Required); req.IntersectWith(validated.Keys); if (req.Count != _schema.Required.Count()) { var actual = String.Join(", ", req.ToArray()); var expected = String.Join(", ", _schema.Required); var msg = state.CreateMessage("Lack of required fields(Actual: [{0}]; Expected: [{1}])", actual, expected); return(new ConstraintsViolationException(msg)); } } if (_schema.MaxProperties != int.MinValue) { if (!(validated.Count <= _schema.MaxProperties)) { var msg = state.CreateMessage("MaxProperties assertion !({0} <= {1})", validated.Count, _schema.MaxProperties); return(new ConstraintsViolationException(msg)); } } if (_schema.MinProperties != int.MaxValue) { if (!(validated.Count >= _schema.MinProperties)) { var msg = state.CreateMessage("MaxProperties assertion !({0} >= {1})", validated.Count, _schema.MinProperties); return(new ConstraintsViolationException(msg)); } } if (_schema.Dependencies != null) { var strDep = _schema.Dependencies as Dictionary <string, string[]>; if (strDep != null) { foreach (var va in validated) { string[] deps = null; if (strDep.TryGetValue(va.Key, out deps)) { var intersected = ((string[])deps.Clone()).Intersect(validated.Keys); if (intersected.Count() != deps.Count()) { var actual = String.Join(", ", intersected.ToArray()); var expected = String.Join(", ", deps); var msg = state.CreateMessage("Dependencies assertion. Lack of depended fields for {0}(Actual: [{1}]; Expected: [{2}])", va.Key, actual, expected); return(new ConstraintsViolationException(msg)); } } } goto depChecked; } var schemaDep = _schema.Dependencies as Dictionary <string, JsonSchemaAttribute>; if (schemaDep != null) { foreach (var va in validated) { JsonSchemaAttribute ext = null; if (schemaDep.TryGetValue(va.Key, out ext)) { var ex = ext.Validate(v, new State().NestAsElem(va.Key), reg); if (ex != null) { // TODO: var msg = state.CreateMessage("Dependencies assertion. Failed to validation for {0}", va.Key); return(new ConstraintsViolationException(msg, ex)); } } } } depChecked: ; } return(null); }
ConstraintsViolationException ValidateArray(IEnumerable <object> v, State state, JsonSchemaRegistory reg) { var length = v.Count(); if (_schema.MaxItems != int.MinValue) { if (!(length <= _schema.MaxItems)) { var msg = state.CreateMessage("MaxItems assertion !({0} <= {1})", length, _schema.MaxItems); return(new ConstraintsViolationException(msg)); } } if (_schema.MinItems != int.MaxValue) { if (!(length >= _schema.MinItems)) { var msg = state.CreateMessage("MinItems assertion !({0} >= {1})", length, _schema.MinItems); return(new ConstraintsViolationException(msg)); } } if (_schema.UniqueItems) { // O(N^2) to use DeepEquals... var i = 0; foreach (var elem in v) { var j = 0; foreach (var elemS in v) { if (i == j) { continue; } if (TypeHelper.DeepEquals(elem, elemS)) { var msg = state.CreateMessage("UniqueItems assertion: Elements at {0} and {1} are duplicated", i, j); return(new ConstraintsViolationException(msg)); } ++j; } ++i; } } List <object> extraItems = null; if (_schema.Items != null) { if (_schema.Items.GetType().IsArray) { var itemSchemas = (JsonSchemaAttribute[])_schema.Items; var i = 0; foreach (var elem in v) { var itemSchema = itemSchemas.ElementAtOrDefault(i); if (itemSchema == null) { if (extraItems == null) { extraItems = new List <object>(); } extraItems.Add(elem); continue; } var ex = itemSchema.Validate(elem, state.NestAsElem(i), reg); if (ex != null) { return(new ConstraintsViolationException("Items", ex)); } ++i; } } else { var itemSchema = (JsonSchemaAttribute)_schema.Items; var i = 0; foreach (var elem in v) { var ex = itemSchema.Validate(elem, state.NestAsElem(i), reg); if (ex != null) { return(new ConstraintsViolationException("Items", ex)); } ++i; } } } if (_schema.AdditionalItems != null) { if (extraItems != null) { foreach (var elem in extraItems) { var ex = _schema.AdditionalItems.Validate(elem, state, reg); if (ex != null) { return(new ConstraintsViolationException("AdditionalItems", ex)); } } } } return(null); }
internal ConstraintsViolationException Validate(object o, State state, JsonSchemaRegistory reg) { if (_schema.Ref != null) { if (reg == null) { reg = JsonSchemaRegistory.GetDefault(); } var schema = reg.Resolve(_schema.Ref); if (schema == null) { // TODO: throw new NotSupportedException(); } return(schema.Validate(o, state, reg)); } ConstraintsViolationException ex = null; var kind = Node.KindOfValue(o); if (_schema.Type != null) { if (_schema.Type.GetType().IsArray) { var ts = (string[])_schema.Type; var found = false; foreach (var t in ts) { if (ValidateKind(kind, t)) { found = true; break; } } if (!found) { var actual = kind.ToString(); var expected = String.Join(", ", ts); var msg = state.CreateMessage("Type is not contained(Actual: {0}; Expected: [{1}])", actual, expected); return(new ConstraintsViolationException(msg)); } } else { var t = (string)_schema.Type; if (!ValidateKind(kind, t)) { var actual = kind.ToString(); var expected = t.ToString(); var msg = state.CreateMessage("Type is not matched(Actual: {0}; Expected: {1})", actual, expected); return(new ConstraintsViolationException(msg)); } } } if (_schema.Enum != null) { var oEnum = o; if (o != null && TypeHelper.TypeWrap(o.GetType()).IsEnum&& kind == NodeKind.String) { oEnum = TypeHelper.GetStringEnumNameOf(o); } var found = false; foreach (var e in _schema.Enum) { if (TypeHelper.DeepEquals(oEnum, e)) { found = true; break; } } if (!found) { var msg = state.CreateMessage("Enum is not matched"); return(new ConstraintsViolationException(msg)); } } if (_schema.Not != null) { ex = _schema.Not.Validate(o, state, reg); if (ex == null) { var msg = state.CreateMessage("Not"); return(new ConstraintsViolationException(msg)); } } if (_schema.AllOf != null) { var i = 0; foreach (var jsonSchema in _schema.AllOf) { ex = jsonSchema.Validate(o, state, reg); if (ex != null) { var msg = state.CreateMessage("AllOf[{0}] is failed", i); return(new ConstraintsViolationException(msg, ex)); } ++i; } } if (_schema.AnyOf != null) { var schemaChecked = false; foreach (var jsonSchema in _schema.AnyOf) { ex = jsonSchema.Validate(o, state, reg); if (ex == null) { schemaChecked = true; break; } } if (!schemaChecked) { var msg = state.CreateMessage("None of AnyOf is matched"); return(new ConstraintsViolationException(msg)); } } if (_schema.OneOf != null) { var checkedI = -1; var i = 0; foreach (var jsonSchema in _schema.OneOf) { ex = jsonSchema.Validate(o, state, reg); if (ex == null) { if (checkedI != -1) { var msg = state.CreateMessage("Both of OneOf[{0}] and OneOf[{1}] are matched", checkedI, i); return(new ConstraintsViolationException(msg)); } checkedI = i; } ++i; } if (checkedI == -1) { var msg = state.CreateMessage("None of AnyOf is matched"); return(new ConstraintsViolationException(msg)); } } switch (kind) { case NodeKind.Boolean: break; case NodeKind.Float: case NodeKind.Integer: ex = ValidateNumber(Convert.ToDouble(o), state, reg); if (ex != null) { return(new ConstraintsViolationException("Number", ex)); } break; case NodeKind.String: var oConverted = (o != null && TypeHelper.TypeWrap(o.GetType()).IsEnum) ? TypeHelper.GetStringEnumNameOf(o) : (string)o; ex = ValidateString(oConverted, state, reg); if (ex != null) { return(new ConstraintsViolationException("String", ex)); } break; case NodeKind.Array: ex = ValidateArray(TypeHelper.ToIEnumerable(o), state, reg); if (ex != null) { return(new ConstraintsViolationException("Array", ex)); } break; case NodeKind.Object: ex = ValidateObject(o, state, reg); if (ex != null) { return(new ConstraintsViolationException("Object", ex)); } break; case NodeKind.Null: break; default: throw new NotImplementedException(kind.ToString()); } return(null); }
public ConstraintsViolationException Validate(object o, JsonSchemaRegistory reg = null) { return(Validate(o, new State(), reg)); }
public static ConstraintsViolationException Validate(this JsonSchemaAttribute j, object o, JsonSchemaRegistory reg = null) { return((new JsonSchemaValidator(j)).Validate(o, reg)); }
public static JsonSchemaAttribute CreateFromType(Type ty, JsonSchemaRegistory reg = null, bool asRef = false) { var kind = Node.KindOfType(ty); switch (kind) { case NodeKind.Boolean: return(new JsonSchemaAttribute { Type = "boolean", }); case NodeKind.Integer: object[] enumsForInteger = null; if (TypeHelper.TypeWrap(ty).IsEnum) { enumsForInteger = System.Enum.GetValues(ty).Cast <object>().ToArray(); } return(new JsonSchemaAttribute { Type = "integer", Enum = enumsForInteger, }); case NodeKind.Float: return(new JsonSchemaAttribute { Type = "number", }); case NodeKind.String: object[] enumsForString = null; if (TypeHelper.TypeWrap(ty).IsEnum) { enumsForString = TypeHelper.GetStringEnumNames(ty); } return(new JsonSchemaAttribute { Type = "string", Enum = enumsForString, }); case NodeKind.Array: var elemTy = TypeHelper.ElemTypeOfIEnumerable(ty); return(new JsonSchemaAttribute { Type = "array", Items = elemTy != null?CreateFromType(elemTy, reg, true) : null, }); case NodeKind.Object: if (ty == typeof(object)) { return(new JsonSchemaAttribute()); } if (TypeHelper.TypeWrap(ty).IsGenericType&& ty.GetGenericTypeDefinition() == typeof(Dictionary <,>)) { return(new JsonSchemaAttribute { Type = "object", }); } break; default: throw new NotImplementedException(); } if (reg == null) { reg = JsonSchemaRegistory.GetDefault(); } var schema = TypeHelper.GetCustomAttribute <JsonSchemaAttribute>(ty); if (schema == null) { schema = new JsonSchemaAttribute(); } schema.Type = "object"; var schemaId = schema.Id; if (schemaId == null) { schemaId = ty.ToString(); } var refSchema = reg.Resolve(schemaId); if (refSchema != null) { schema = refSchema; goto skip; } else { reg.Register(schemaId, schema); } var baseType = TypeHelper.TypeWrap(ty).BaseType; HashSet <string> baseFieldNames = null; if (baseType != null) { Type schemaBaseType; if (RefChecker.IsRefTag(baseType, out schemaBaseType)) { var baseSchemaValue = CreateFromType(schemaBaseType, reg, false); schema.Type = baseSchemaValue.Type; goto skip; } // Nest fields included in the base class var baseSchema = CreateFromType(baseType, reg, true); if (baseSchema != null && baseSchema.Ref != null) { schema.AddToAllOf(baseSchema); var baseFields = TypeHelper.TypeWrap(baseType).GetFields(BindingFlags.Public | BindingFlags.Instance); baseFieldNames = new HashSet <string>(baseFields.Select(f => f.Name)); } } var properties = new Dictionary <string, JsonSchemaAttribute>(); var required = new List <string>(); var dependencies = new Dictionary <string, string[]>(); var fields = TypeHelper.TypeWrap(ty).GetFields(BindingFlags.Public | BindingFlags.Instance); foreach (var field in fields) { var fieldType = field.FieldType; JsonSchemaAttribute fieldSchema = null; var attr = TypeHelper.GetCustomAttribute <JsonFieldAttribute>(field); var elemName = JsonFieldAttribute.FieldName(attr, field); // TODO: duplication check // If elements are also included in Base classes, skip collecting a schema for the elements. if (baseFieldNames != null && baseFieldNames.Contains(field.Name)) { fieldSchema = new JsonSchemaAttribute(); goto skipField; } fieldSchema = TypeHelper.GetCustomAttribute <JsonSchemaAttribute>(field); if (fieldSchema == null) { fieldSchema = new JsonSchemaAttribute(); } var fieldItemsSchema = TypeHelper.GetCustomAttribute <ItemsJsonSchemaAttribute>(field); if (fieldItemsSchema != null) { fieldSchema.Items = fieldItemsSchema; } if (attr != null && attr.DynamicResolverTag != null) { if (!TypeHelper.TypeWrap(fieldType).IsGenericType || fieldType.GetGenericTypeDefinition() != typeof(Dictionary <,>)) { var baseMsg = "A type of the field which has DynamicResolver must be a Dictionary<,>"; var msg = string.Format("{0}: Type = {1} at \"{2}\" of {3}", baseMsg, fieldType, elemName, ty); throw new ArgumentException(msg); } var keyType = TypeHelper.TypeWrap(fieldType).GetGenericArguments()[0]; if (keyType != typeof(string)) { var baseMsg = "A key of the dictionary which has DynamicResolver must be a string type"; var msg = string.Format("{0}: KeyType = {1} at \"{2}\" of {3}", baseMsg, keyType, elemName, ty); throw new ArgumentException(msg); } fieldSchema._dynamicResolverTag = attr.DynamicResolverTag; } var fieldItemRequired = TypeHelper.GetCustomAttribute <JsonSchemaRequiredAttribute>(field); if (fieldItemRequired != null) { required.Add(elemName); } var fieldItemDependencies = TypeHelper.GetCustomAttribute <JsonSchemaDependenciesAttribute>(field); if (fieldItemDependencies != null) { dependencies.Add(elemName, fieldItemDependencies.Dependencies); } var fieldTypeSchema = CreateFromType(fieldType, reg, true); if (fieldTypeSchema.Ref != null) { fieldSchema = fieldTypeSchema; } else { // Update if (fieldSchema.Type == null) { fieldSchema.Type = fieldTypeSchema.Type; } if (fieldSchema.Enum == null) { fieldSchema.Enum = fieldTypeSchema.Enum; } if (fieldTypeSchema.Items != null) { var fieldTypeSchemaItems = fieldTypeSchema.Items as JsonSchemaAttribute; if (fieldTypeSchemaItems.Ref != null) { fieldSchema.Items = fieldTypeSchemaItems; } else { if (fieldTypeSchemaItems.Type != null) { var fieldSchemaItems = fieldSchema.Items as JsonSchemaAttribute; if (fieldSchemaItems != null) { fieldSchemaItems.Type = fieldTypeSchemaItems.Type; } else { fieldSchema.Items = new JsonSchemaAttribute { Type = fieldTypeSchemaItems.Type, }; } } if (fieldTypeSchemaItems.Enum != null) { var fieldSchemaItems = fieldSchema.Items as JsonSchemaAttribute; fieldSchemaItems.Enum = fieldTypeSchemaItems.Enum; } } } } // Add custom refs to AllOf not to override constrains which already existing. var customRef = TypeHelper.GetCustomAttribute <JsonSchemaRefAttribute>(field); if (customRef != null) { Type schemaBaseType; if (!RefChecker.IsRefTagDerived(customRef.TagType, out schemaBaseType)) { throw new ArgumentException("IRefTag<T> must be derived by tagType"); } var customSchema = CreateFromType(customRef.TagType, reg, true); switch (customRef.Influence) { case InfluenceRange.Entiry: fieldSchema.AddToAllOf(customSchema); break; case InfluenceRange.AdditionalProperties: if (fieldSchema.AdditionalProperties == null) { fieldSchema.AdditionalProperties = new JsonSchemaAttribute(); } fieldSchema.AdditionalProperties.AddToAllOf(customSchema); break; } } // Add custom refs to AllOf not to override constrains which already existing. var customItemsRef = TypeHelper.GetCustomAttribute <ItemsJsonSchemaRefAttribute>(field); if (customItemsRef != null) { Type schemaBaseType; if (!RefChecker.IsRefTagDerived(customItemsRef.TagType, out schemaBaseType)) { throw new ArgumentException("IRefTag<T> must be derived by tagType"); } var customSchema = CreateFromType(customItemsRef.TagType, reg, true); switch (customItemsRef.Influence) { case InfluenceRange.Entiry: if (fieldSchema.Items == null) { fieldSchema.Items = new JsonSchemaAttribute(); } ((JsonSchemaAttribute)fieldSchema.Items).AddToAllOf(customSchema); break; case InfluenceRange.AdditionalProperties: if (fieldSchema.Items == null) { fieldSchema.Items = new JsonSchemaAttribute(); } if (((JsonSchemaAttribute)fieldSchema.Items).AdditionalProperties == null) { ((JsonSchemaAttribute)fieldSchema.Items).AdditionalProperties = new JsonSchemaAttribute(); } ((JsonSchemaAttribute)fieldSchema.Items).AdditionalProperties.AddToAllOf(customSchema); break; } } skipField: properties.Add(elemName, fieldSchema); } schema.Properties = properties; if (required.Count != 0) { schema.Required = required.ToArray(); } if (dependencies.Count != 0) { schema.Dependencies = dependencies; } skip: if (asRef) { return(new JsonSchemaAttribute { Ref = schemaId, }); } return(schema); }
public static JsonSchemaAttribute CreateFromClass <T>(JsonSchemaRegistory reg = null, bool asRef = false) { return(CreateFromType(typeof(T), reg, asRef)); }