private KeyValuePair <SchemaRegex, Subschema>[] ReadPatternSchemas(object v, MPSchema doc) { var map = (KeyValuePair <string, object>[])v; var result = new KeyValuePair <SchemaRegex, Subschema> [map.Length]; for (int i = 0; i < map.Length; i++) { string key = map[i].Key; var regex = SchemaRegexCache.Lookup(key); if (regex == null) { return(null); } var v2 = From(map[i].Value, doc); if (v2 == null) { return(null); } result[i] = new KeyValuePair <SchemaRegex, Subschema>(regex, v2); } return(result); }
/// <summary> /// Builds a schema document from a JsonLite object tree /// </summary> /// <param name="schema"></param> /// <param name="doc"></param> /// <returns></returns> public Subschema From(object json, MPSchema doc) { var map = json as KeyValuePair <string, object>[]; if (map == null || map.Length == 0) { if (map != null) { return(SchemaConstants.Everything); } if (json is bool b) { return(b ? SchemaConstants.Everything : SchemaConstants.Nothing); } return(null); } var subschema = new Subschema { data = new object[(int)Keyword.MAX_STORED], Flags = SchemaFlags.TypeAll | SchemaFlags.Uncompressed }; bool exclusiveMinimum = false; bool exclusiveMaximum = false; bool isNothing = false; List <Pair> metadata = null; foreach (var kv in map) { var k = KeywordUtils.ParseKeyword(kv.Key); object v = kv.Value; Subschema child; Subschema[] list; string name; object[] array; string[] stringList; double d; switch (k) { case Keyword.AllOf: list = ReadSchemaList(v, doc); if (list == null || list.Length < 1) { return(null); } subschema.AllOf = list; break; case Keyword.AnyOf: list = ReadSchemaList(v, doc); if (list == null || list.Length < 1) { return(null); } subschema.AnyOf = list; break; case Keyword.OneOf: list = ReadSchemaList(v, doc); if (list == null || list.Length < 1) { return(null); } subschema.OneOf = list; break; case Keyword.Const: if (!doc.LimitVersion(SchemaVersion.Draft6, true)) { return(null); } subschema.Const = MPJson.From(v); break; case Keyword.Enum: array = v as object[]; if (array == null || array.Length < 1) { return(null); } subschema.Enum = MPJson.From(array); break; case Keyword.Not: child = From(v, doc); if (child == null) { return(null); } subschema.Not = child; if (map.Length == 1) { if (child.Type == TypeFlags.None) { return(SchemaConstants.Everything); } if (child == SchemaConstants.Everything) { return(SchemaConstants.Nothing); } } break; case Keyword.Type: var flags = subschema.Flags & ~SchemaFlags.TypeAll; if (v is string) { flags = ToTypeFlag((string)v); if (flags == 0) { return(null); } } else { stringList = ReadStringList(v); if (stringList == null) { return(null); } foreach (string type in stringList) { var flag = ToTypeFlag(type); if (flag == 0) { return(null); } flags |= flag; } } subschema.Flags = (subschema.Flags & ~SchemaFlags.TypeAll) | flags; break; case Keyword.If: if (!doc.LimitVersion(SchemaVersion.Draft7, true)) { return(null); } child = From(v, doc); if (child == null) { return(null); } subschema.If = child; break; case Keyword.Then: if (!doc.LimitVersion(SchemaVersion.Draft7, true)) { return(null); } child = From(v, doc); if (child == null) { return(null); } subschema.Then = child; break; case Keyword.Else: if (!doc.LimitVersion(SchemaVersion.Draft7, true)) { return(null); } child = From(v, doc); if (child == null) { return(null); } subschema.Else = child; break; // Number attributes case Keyword.ExclusiveMaximum: if (v is double && doc.LimitVersion(SchemaVersion.Draft6, true)) { subschema.ExclusiveMaximum = (double)v; } else if (v is bool && doc.LimitVersion(SchemaVersion.Draft4, false)) { exclusiveMaximum = (bool)v; } else { return(null); } break; case Keyword.ExclusiveMinimum: if (v is double && doc.LimitVersion(SchemaVersion.Draft6, true)) { subschema.ExclusiveMinimum = (double)v; } else if (v is bool && doc.LimitVersion(SchemaVersion.Draft4, false)) { exclusiveMinimum = (bool)v; } else { return(null); } break; case Keyword.Maximum: if (!(v is double)) { return(null); } if (exclusiveMaximum) { subschema.ExclusiveMaximum = (double)v; } else { subschema.Maximum = (double)v; } break; case Keyword.Minimum: if (!(v is double)) { return(null); } if (exclusiveMinimum) { subschema.ExclusiveMinimum = (double)v; } else { subschema.Minimum = (double)v; } break; case Keyword.MultipleOf: if (!(v is double) || (d = (double)v) <= 0) { return(null); } subschema.MultipleOf = d; break; // String attributes case Keyword.Format: name = v as string; if (name == null) { return(null); } subschema.Format = name; break; case Keyword.MaxLength: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MaxLength = (double)v; break; case Keyword.MinLength: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MinLength = (double)v; break; case Keyword.Pattern: name = v as string; var regex = SchemaRegexCache.Lookup(name); if (regex == null) { return(null); } subschema.Pattern = regex; break; // Object attributes case Keyword.AdditionalProperties: child = From(v, doc); if (child == null) { return(null); } subschema.AdditionalProperties = child; break; case Keyword.Dependencies: KeyValuePair <string, string[]>[] depRequired; KeyValuePair <string, Subschema>[] schemas; if (!ReadDependencies(v, doc, out schemas, out depRequired)) { return(null); } if (depRequired != null) { subschema.DependentRequired = depRequired; } if (schemas != null) { subschema.DependentSchemas = schemas; } break; case Keyword.DependentRequired: if (!doc.LimitVersion(SchemaVersion.Draft201909, true)) { return(null); } if (!ReadDependencies(v, doc, out schemas, out depRequired)) { return(null); } if (schemas != null) { return(null); } subschema.DependentRequired = depRequired; break; case Keyword.DependentSchemas: if (!doc.LimitVersion(SchemaVersion.Draft201909, true)) { return(null); } schemas = ReadPropertySchemas(v, doc); if (schemas == null) { return(null); } subschema.DependentSchemas = schemas; break; case Keyword.PatternProperties: var patternSchemas = ReadPatternSchemas(v, doc); if (patternSchemas == null) { return(null); } subschema.PatternProperties = patternSchemas; break; case Keyword.Properties: schemas = ReadPropertySchemas(v, doc); if (schemas == null) { return(null); } subschema.Properties = schemas; break; case Keyword.MaxProperties: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MaxProperties = (double)v; break; case Keyword.MinProperties: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MinProperties = (double)v; break; case Keyword.PropertyNames: if (!doc.LimitVersion(SchemaVersion.Draft6, true)) { return(null); } child = From(v, doc); if (child == null) { return(null); } subschema.PropertyNames = child; break; case Keyword.Required: stringList = ReadStringList(v); if (stringList == null) { return(null); } subschema.Required = stringList; break; // Array attributes case Keyword.AdditionalItems: child = From(v, doc); if (child == null) { return(null); } subschema.AdditionalItems = child; break; case Keyword.Contains: if (!doc.LimitVersion(SchemaVersion.Draft6, true)) { return(null); } child = From(v, doc); if (child == null) { return(null); } subschema.Contains = child; break; case Keyword.Items: object result = v is object[]? ReadSchemaList(v, doc) : (object)From(v, doc); if (result == null) { return(null); } subschema.Items = result; break; case Keyword.MaxContains: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MaxContains = (double)v; break; case Keyword.MinContains: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MinContains = (double)v; break; case Keyword.MaxItems: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MaxItems = (double)v; break; case Keyword.MinItems: if (!IsNonnegativeInteger(v)) { return(null); } subschema.MinItems = (double)v; break; case Keyword.UniqueItems: if (!(v is bool)) { return(null); } if ((bool)v) { subschema.UniqueItems = true; } break; // MORE case Keyword._Ref: name = v as string; if (name == null) { return(null); } string reference = doc.ConvertReference(name); var r = subschema.Ref = new SchemaReference(reference); break; case Keyword.Id: if (!doc.LimitVersion(SchemaVersion.Draft4, false)) { return(null); } goto DoId; case Keyword._Id: if (!doc.LimitVersion(SchemaVersion.Draft6, true)) { return(null); } DoId: name = v as string; if (name == null) { return(null); } AddMetadata(ref metadata, kv.Key, v); break; case Keyword.ContentSchema: case Keyword.Default: AddMetadata(ref metadata, kv.Key, v); break; case Keyword.Title: case Keyword.Description: case Keyword._Schema: name = v as string; if (name == null) { return(null); } AddMetadata(ref metadata, kv.Key, v); break; case Keyword.Examples: if (!(v is object[])) { return(null); } AddMetadata(ref metadata, kv.Key, v); break; case Keyword._Comment: case Keyword.ContentEncoding: case Keyword.ContentMediaType: if (!doc.LimitVersion(SchemaVersion.Draft7, true)) { return(null); } name = v as string; if (name == null) { return(null); } AddMetadata(ref metadata, kv.Key, v); break; case Keyword.Deprecated: case Keyword.ReadOnly: case Keyword.WriteOnly: if (!doc.LimitVersion(SchemaVersion.Draft7, true)) { return(null); } if (!(v is bool)) { return(null); } if ((bool)v) { AddMetadata(ref metadata, kv.Key, v); } break; case Keyword._Defs: if (!doc.LimitVersion(SchemaVersion.Draft201909, true)) { return(null); } goto case Keyword.Definitions; case Keyword.Definitions: schemas = ReadPropertySchemas(v, doc); if (schemas == null) { return(null); } AddMetadata(ref metadata, kv.Key, schemas); break; } } // Ignore all other keywords if ref is present // Draft8 might have changed this, // but it is not reflected in the standard test suite if ((subschema.Flags & SchemaFlags._Ref) != 0) { subschema.Flags &= ~SchemaFlags.StoredProperties; subschema.Flags |= SchemaFlags.TypeAll | SchemaFlags._Ref; } if (metadata != null) { AssignMetadata(subschema, metadata); } CompressData(subschema); return(subschema); }