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));
        }
Example #2
0
        /// <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);
        }