private static void ReplaceRefs(JSONSchema schema, JSONSchema root) { ReplaceRefsInDictionary(schema.Properties, root); ReplaceRefsInDictionary(schema.Definitions, root); ReplaceRefsInDictionary(schema.PatternProperties, root); if (schema.AdditionalProperties != null) { ReplaceRefsInAnyOf <bool, JSONSchema>(schema.AdditionalProperties, root); } if (schema.AdditionalItems != null) { ReplaceRefsInAnyOf <bool, JSONSchema>(schema.AdditionalItems, root); } if (schema.Items != null) { ReplaceRefsInAnyOf <JSONSchema, SchemaArray>(schema.Items, root); } ReplaceRefsInSchemaArray(schema.AnyOf, root); ReplaceRefsInSchemaArray(schema.AllOf, root); ReplaceRefsInSchemaArray(schema.OneOf, root); if (schema.Not?.Ref != null) { schema.Not = convertRefToSchema(schema.Not.Ref, root); } }
private static void ReplaceRefsInSchemaArray(SchemaArray array, JSONSchema root) { if (array?.Count > 0) { for (int i = 0; i < array.Count; i++) { if (array[i]?.Ref != null) { array[i] = convertRefToSchema(array[i].Ref, root); } else { ReplaceRefs(array[i], root); } } } }
private Type ComputeType(JSONSchema schema) { if (schema.Type?.GetUnderlyingType() == typeof(SimpleType)) { var simpleType = schema.Type.Value as SimpleType; switch (simpleType.Value) { case SimpleType.Boolean: return(typeof(Boolean)); case SimpleType.String: return(typeof(string)); case SimpleType.Integer: return(typeof(Int32)); case SimpleType.Number: return(typeof(Decimal)); case SimpleType.Object: return(typeof(object)); case SimpleType.Array: if (schema.Items?.Value is JSONSchema) { var itemsSchema = schema.Items.Value as JSONSchema; if (context.ContainsKey(itemsSchema)) { return(typeof(List <>).MakeGenericType(context[itemsSchema].Type)); } else { return(typeof(List <>).MakeGenericType(ComputeType(itemsSchema))); } } return(typeof(IList)); default: return(typeof(object)); } } return(typeof(object)); }
private static JSONSchema convertRefToSchema(string @ref, JSONSchema root) { if (@ref.Equals("#")) { return(root); } else { var paths = @ref.Split('/'); if (paths.Length == 3 && paths[0].Equals("#") && paths[1].Equals("definitions")) { return(root.Definitions[paths[2]]); } else { throw new NotImplementedException("this type of reference is not supported"); } } }
private static void ReplaceRefsInAnyOf <T1, T2>(AnyOf <T1, T2> anyOf, JSONSchema root) { if (anyOf?.GetValue() is JSONSchema) { var anyOfSchema = (JSONSchema)anyOf.GetValue(); if (anyOfSchema?.Ref != null) { anyOf.ChangeValue(convertRefToSchema(anyOfSchema.Ref, root)); } else { ReplaceRefs(anyOfSchema, root); } } if (anyOf?.GetValue() is SchemaArray) { var anyOfSchema = (SchemaArray)anyOf.GetValue(); ReplaceRefsInSchemaArray(anyOfSchema, root); } }
public static JSONSchema MergeSchemas(SchemaArray schemas) { JSONSchema result = new JSONSchema(); //handle recursivity schemas = new SchemaArray( schemas.Where(x => x.AllOf == null || !x.AllOf.Any()) .Union( schemas.Where(x => x.AllOf != null && x.AllOf.Any()) .Select(x => MergeSchemas(x.AllOf))).ToList()); //This won't raise an exception if different (incompatible) types are defined result.Type = schemas.Where(x => x.Type != null).Select(x => x.Type).FirstOrDefault(); var multiples = schemas.Where(x => x.MultipleOf.HasValue).Select(x => x.MultipleOf.Value); if (multiples.Any()) { result.MultipleOf = multiples.Aggregate((uint)1, (lcm, next) => LCM(lcm, next)); } var max = schemas.Where(x => x.Maximum.HasValue).Select(x => x.Maximum); if (max.Any()) { result.Maximum = max.Min(); result.ExclusiveMaximum = schemas.Where(x => x.ExclusiveMaximum.HasValue && x.Maximum == result.Maximum).Select(x => x.ExclusiveMaximum).FirstOrDefault(); } var min = schemas.Where(x => x.Mininum.HasValue).Select(x => x.Mininum); if (min.Any()) { result.Mininum = min.Max(); result.ExclusiveMinimum = schemas.Where(x => x.ExclusiveMinimum.HasValue && x.Mininum == result.Mininum).Select(x => x.ExclusiveMinimum).FirstOrDefault(); } var maxLength = schemas.Where(x => x.MaxLength.HasValue).Select(x => x.MaxLength); if (maxLength.Any()) { result.MaxLength = maxLength.Min(); } var minLength = schemas.Where(x => x.MinLength.HasValue).Select(x => x.MinLength); if (minLength.Count() > 0) { result.MinLength = minLength.Max(); } var patterns = schemas.Where(x => !string.IsNullOrEmpty(x.Pattern)).Select(x => x.Pattern); if (patterns.Any()) { result.Pattern = string.Concat(patterns.Select(x => string.Format("(?={0})", x))); } //TODO: items var maxItems = schemas.Where(x => x.MaxItems.HasValue).Select(x => x.MaxItems); if (maxItems.Any()) { result.MaxItems = maxItems.Min(); } var minItems = schemas.Where(x => x.MinItems.HasValue).Select(x => x.MinItems); if (minItems.Any()) { result.MinItems = minItems.Max(); } var properties = schemas.Where(x => x.Properties != null && x.Properties.Any()).Select(x => x.Properties); if (properties.Any()) { result.Properties = properties.SelectMany(dict => dict) .ToLookup(pair => pair.Key, pair => pair.Value) .ToDictionary(group => group.Key, group => group.First()); } return(result); }
private static void ReplaceRefsInDictionary(Dictionary <string, JSONSchema> dictionary, JSONSchema root) { if (dictionary != null) { var keyList = new List <string>(dictionary.Keys); foreach (string property in keyList) { if (dictionary[property]?.Ref != null) { dictionary[property] = convertRefToSchema(dictionary[property].Ref, root); } else { ReplaceRefs(dictionary[property], root); } } } }
private Type GetEnumType(JSONSchema schema) { if (schema.Type != null && schema.Type.Value is SimpleType) { return(ComputeType(schema)); } if (schema.Type == null) { HashSet <JsonValueKind> types = new HashSet <JsonValueKind>(); foreach (JsonElement enumValue in schema.Enum) { types.Add(enumValue.ValueKind); } if (types.Count() == 1) { switch (types.First()) { case JsonValueKind.String: return(typeof(string)); case JsonValueKind.Number: return(NumberDefaultType); case JsonValueKind.True: case JsonValueKind.False: return(typeof(bool)); default: return(typeof(object)); } } if (types.Count() == 2) { if (types.Contains(JsonValueKind.False) && types.Contains(JsonValueKind.True)) { return(typeof(bool)); } if (types.Contains(JsonValueKind.Null)) { switch (types.Where(x => x != JsonValueKind.Null).First()) { case JsonValueKind.String: return(typeof(string)); //strings can null case JsonValueKind.Number: return(typeof(Nullable <>).MakeGenericType(NumberDefaultType)); case JsonValueKind.True: case JsonValueKind.False: return(typeof(Nullable <>).MakeGenericType(typeof(bool))); default: return(typeof(object)); //objects can be null } } } if (types.Count() == 3) { if (types.IsSubsetOf(new List <JsonValueKind> { JsonValueKind.Null, JsonValueKind.False, JsonValueKind.True })) { return(typeof(Nullable <>).MakeGenericType(typeof(bool))); } } } return(typeof(object)); }
private CodeTypeDeclaration GenerateClass() { if (!context.ContainsKey(schema)) { context.Add(schema, new GenerationResult() { TypeName = targetClass.Name, Type = MyTypeBuilder.CreateType(targetClass.Name), ClassGenerator = this }); } if (schema.Enum?.Count > 0) { return(GenerateClassFromEnumSchema()); } if (schema.Type?.Value is SimpleType && (schema.Type.Value as SimpleType).Value != SimpleType.Object) { var schemaType = schema.Type.Value as SimpleType; if (schemaType.Value == SimpleType.Integer) { return(GenerateClassFromIntegerSchema()); } if (schemaType.Value == SimpleType.Number) { return(GenerateClassFromNumberSchema()); } if (schemaType.Value == SimpleType.String) { return(GenerateClassFromStringSchema()); } if (schemaType.Value == SimpleType.Array) { return(GenerateClassFromArraySchema()); } } var definitions = schema.Definitions; if (definitions != null) { foreach (string definitionName in schema.Definitions.Keys) { var definition = definitions[definitionName]; if (!context.ContainsKey(definition)) { var nestedClassGenerator = new ClassGeneratorFromJsonSchema(definition, context); context[definition] = new GenerationResult() { Type = MyTypeBuilder.CreateType(nestedClassGenerator.targetClass.Name), TypeName = nestedClassGenerator.targetClass.Name, ClassGenerator = nestedClassGenerator }; nestedClassGenerator.GenerateClass(); } //context.Add(definition, new GenerationResult() { TypeName = nestedClassGenerator.targetClass.Name }); } } var properties = schema.Properties; var additionalProperties = schema.AdditionalProperties; //Oneof/AnyOf/AllOf are only supported when there are no properties or additionalProperties and when the schema type is not a primitive type or array type if (properties == null && additionalProperties == null) { if (schema.OneOf != null && schema.OneOf.Count > 0) { return(GenerateClassFromOneOfAnyOfSchema(true)); } if (schema.AnyOf != null && schema.AnyOf.Count > 0) { return(GenerateClassFromOneOfAnyOfSchema(false)); } if (schema.AllOf != null && schema.AllOf.Count > 0) { var mergedSchema = JSONSchema.MergeSchemas(schema.AllOf); var mergedClassGenerator = new ClassGeneratorFromJsonSchema(mergedSchema, this.targetClass.Name); targetClass = mergedClassGenerator.GenerateClass(); context[schema] = mergedClassGenerator.context[mergedSchema]; foreach (var jsonSchema in mergedClassGenerator.context.Keys) { if (jsonSchema != mergedSchema) { context[jsonSchema] = mergedClassGenerator.context[jsonSchema]; } } return(targetClass); } } if (properties != null) { foreach (string propertyName in properties.Keys) { var cleanPropertyName = Clean(propertyName); if (propertyNames.Contains(cleanPropertyName)) { //to avoid property names that would collide continue; } propertyNames.Add(cleanPropertyName); var property = properties[propertyName]; if (context.ContainsKey(property)) { targetClass.AddProperty(cleanPropertyName, context[property].TypeName); } else if (property.Type?.GetUnderlyingType() == typeof(SimpleType) && (property.Type.Value as SimpleType).Value != SimpleType.Object && (property.Type.Value as SimpleType).Value != SimpleType.Array && (property.Enum == null || property.Enum.Count < 1) ) { targetClass.AddProperty(cleanPropertyName, ComputeType((SimpleType)property.Type.GetValue())); } else { var nestedClassGenerator = new ClassGeneratorFromJsonSchema(property, context, cleanPropertyName); context[property] = new GenerationResult() { Type = MyTypeBuilder.CreateType(nestedClassGenerator.targetClass.Name), TypeName = nestedClassGenerator.targetClass.Name, ClassGenerator = nestedClassGenerator }; nestedClassGenerator.GenerateClass(); targetClass.AddProperty(cleanPropertyName, context[property].Type); } } } if (additionalProperties != null) { if (additionalProperties.Value is bool) { if ((bool)additionalProperties.Value == true) { targetClass.BaseTypes.Add(new CodeTypeReference(typeof(Dictionary <string, object>))); } } else { var additionalPropertiesSchema = additionalProperties.Value as JSONSchema; if (!context.ContainsKey(additionalPropertiesSchema)) { var nestedClassGenerator = new ClassGeneratorFromJsonSchema(additionalPropertiesSchema, context, context[schema].TypeName + "AdditionalProperties"); context[additionalPropertiesSchema] = new GenerationResult() { Type = MyTypeBuilder.CreateType(nestedClassGenerator.targetClass.Name), TypeName = nestedClassGenerator.targetClass.Name, ClassGenerator = nestedClassGenerator }; nestedClassGenerator.GenerateClass(); } targetClass.BaseTypes.Add(new CodeTypeReference("Dictionary", new CodeTypeReference(typeof(string)), new CodeTypeReference(context[additionalPropertiesSchema].Type))); context[schema].Imports.Add("System.Collections.Generic"); } } return(targetClass); }
private ClassGeneratorFromJsonSchema(JSONSchema schema, Dictionary <JSONSchema, GenerationResult> context, string title = null) : base(title ?? schema.Title) { this.schema = schema; this.context = context; }
public ClassGeneratorFromJsonSchema(JSONSchema schema, string title = null) : base(title ?? schema.Title) { this.schema = schema; context = new Dictionary <JSONSchema, GenerationResult>(); }