Пример #1
0
        public JsonSchemaRefAttribute(Type tagType, InfluenceRange influence = InfluenceRange.Entiry)
        {
            Type schemaBaseType;

            if (!RefChecker.IsRefTagDerived(tagType, out schemaBaseType))
            {
                throw new ArgumentException("IRefTag<T> must be derived by tagType");
            }

            TagType   = tagType;
            Influence = influence;
        }
Пример #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);
        }