private KeyValuePair <string, Subschema>[] ReadPropertySchemas(object v, MPSchema doc) { var map = (KeyValuePair <string, object>[])v; var result = new KeyValuePair <string, Subschema> [map.Length]; for (int i = 0; i < map.Length; i++) { string key = map[i].Key; var v2 = From(map[i].Value, doc); if (v2 == null) { return(null); } result[i] = new KeyValuePair <string, Subschema>(key, v2); } return(result); }
private Subschema[] ReadSchemaList(object v, MPSchema doc) { object[] array = v as object[]; if (array == null) { return(null); } var list = new Subschema[array.Length]; for (int i = 0; i < array.Length; i++) { Subschema child = From(array[i], doc); if (child == null) { return(null); } list[i] = child; } return(list); }
public SchemaBuilder(MPSchema doc, MPJson json) { this.Document = doc; this.Json = json; // Find references GatherIdsAndReferences(json.Value); // Build schemas Root = From(json.Value, doc); string rootReference = doc.ConvertReference(""); References.Remove(rootReference); Definitions[rootReference] = new SchemaReference(rootReference, Root); foreach (string r in References) { if (!Ids.TryGetValue(r, out object value)) { value = JsonPointer.Find(json, r); } Definitions[r] = new SchemaReference(r, value != null ? From(value, doc) : null) { Id = Definitions.Count }; } // Fix up references foreach (var reference in Definitions.Values) { if (reference.Schema != null) { ReplaceReferences(reference.Schema); } } // Compression }
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); }
private bool ValidateGeneric(Subschema schema, object instance) { bool finalResult = true; for (SchemaFlags mask = schema.Flags & (SchemaFlags.GenericProperties & SchemaFlags.AllProperties); mask != 0; mask = (SchemaFlags)RemoveLowestBit((long)mask)) { Keyword keyword = (Keyword)IndexOfLowestBit((long)mask); bool? result = null; Subschema[] schemaArray; int index; switch (keyword) { case Keyword.If: int level = TurnOffErrors(); result = Validate(schema.If, instance, Keyword.If); RestoreSuppressionLevel(level); result = result == true ? Validate(schema.Then, instance, Keyword.Then) : Validate(schema.Else, instance, Keyword.Else); break; case Keyword.Not: level = TurnOffErrors(); result = !Validate(schema.Not, instance, Keyword.Not); RestoreSuppressionLevel(level); break; case Keyword.AllOf: result = true; schemaArray = schema.AllOf; SchemaPointer.Push(keyword); for (index = 0; index < schemaArray.Length; index++) { if (!Validate(schemaArray[index], instance, index)) { finalResult = false; if (!IsRecordingErrors) { break; } } } SchemaPointer.Pop(); break; case Keyword.AnyOf: result = false; level = TurnOffErrors(); schemaArray = schema.AnyOf; SchemaPointer.Push(keyword); for (index = 0; index < schemaArray.Length; index++) { if (Validate(schemaArray[index], instance, index)) { result = true; break; } } SchemaPointer.Pop(); RestoreSuppressionLevel(level); break; case Keyword.OneOf: result = false; level = TurnOffErrors(); schemaArray = schema.OneOf; SchemaPointer.Push(keyword); for (index = 0; index < schemaArray.Length; index++) { if (Validate(schemaArray[index], instance, index)) { result = !result; if (result == false) { break; } } } SchemaPointer.Pop(); RestoreSuppressionLevel(level); break; case Keyword.Enum: result = false; // TODO: Provide support for type coercion foreach (object v in (object[])schema.Enum.Value) { if (MPJson.Matches(v, instance)) { result = true; break; } } break; case Keyword.Const: // TODO: Provide support for string to value coercion result = MPJson.Matches(schema.Const.Value, instance); break; case Keyword._Ref: var r = schema.Ref; if (r.Version != 0) { var draft = new MPSchema(MPJson.From(instance), r.Version); result = draft.IsValid; break; // This was checked on creation } var rSchema = r.Schema; result = rSchema != null && ReferenceUsageIsControlled(); if (result == true && !Validate(rSchema, instance, Keyword._Ref)) { finalResult = false; } break; case Keyword.Metadata: break; } if (result == false) { finalResult = false; if (ReportError(keyword, schema, instance)) { return(false); } } } return(finalResult); }
public bool Validate(MPSchema schema, MPJson json, EventHandler <ValidationArgs> eventHandler = null) { return(Validate(schema?.Root, json, eventHandler)); }
/// <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); }
/// <summary> /// Validate json using schema /// </summary> /// <param name="json"></param> /// <param name="schema"></param> /// <param name="args"></param> /// <returns></returns> public static bool Validate(this MPJson json, MPSchema schema, EventHandler <ValidationArgs> args) { return(schema.Validate(json, args)); }