Пример #1
0
        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);
        }
Пример #2
0
        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);
        }