private bool ValidateType(Subschema schema, object value) { var type = MPJson.GetType(value); switch (type) { case JsonType.Object: if ((schema.Flags & SchemaFlags.TypeObject) == 0) { break; } return(ValidateObject(schema, (KeyValuePair <string, object>[])value)); case JsonType.Array: if ((schema.Flags & SchemaFlags.TypeArray) == 0) { break; } return(ValidateArray(schema, (object[])value)); case JsonType.Number: double d = Convert.ToDouble(value); if ((schema.Flags & SchemaFlags.TypeNumber) == 0 && ((schema.Flags & SchemaFlags.TypeInteger) == 0 || d != Math.Floor(d))) { break; } return(ValidateNumber(schema, d)); case JsonType.String: if ((schema.Flags & SchemaFlags.TypeString) != 0) { return(ValidateString(schema, (string)value)); } if (this.CoerceStringsToValues) { var str = (string)value; if ((schema.Flags & (SchemaFlags.TypeNumber | SchemaFlags.TypeInteger)) != 0 && double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out d) && (0 != (schema.Flags & SchemaFlags.TypeNumber) || d == Math.Floor(d))) { return(ValidateNumber(schema, d)); } if ((schema.Flags & SchemaFlags.TypeBoolean) != 0 && string.Equals(str, str.Length == 4 ? JsonParser.TrueKeyword : JsonParser.FalseKeyword, StringComparison.OrdinalIgnoreCase)) { return(true); } } break; case JsonType.Boolean: if ((schema.Flags & SchemaFlags.TypeBoolean) == 0) { break; } return(true); case JsonType.Null: if ((schema.Flags & SchemaFlags.TypeNull) == 0) { break; } return(true); } if (!IsRecordingErrors) { return(false); } return((schema.Flags & SchemaFlags.TypeAll) != 0 ? ReportError( ErrorType.Type, schema, actual: KeywordUtils.GetTypeText(type), expected: schema.GetValidTypes(), instance: value) : ReportError(ErrorType.None, actual: value)); }
/// <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); }