protected ValidationError CreateError(string message, ErrorType errorType, JSchema schema, object value, IList<ValidationError> childErrors, IJsonLineInfo lineInfo, string path)
        {
            if (_schemaDiscovery == null)
            {
                _schemaDiscovery = new JSchemaDiscovery();
                _schemaDiscovery.Discover(Schema, null);
            }

            ValidationError error = new ValidationError();
            error.Message = message;
            error.ErrorType = errorType;
            error.Path = path;
            if (lineInfo != null)
            {
                error.LineNumber = lineInfo.LineNumber;
                error.LinePosition = lineInfo.LinePosition;
            }
            error.Schema = schema;
            error.SchemaId = _schemaDiscovery.KnownSchemas.Single(s => s.Schema == schema).Id;
            error.SchemaBaseUri = schema.BaseUri;
            error.Value = value;
            error.ChildErrors = childErrors;

            return error;
        }
        public void RaiseError(IFormattable message, ErrorType errorType, JSchema schema, object value, IList<ValidationError> childErrors)
        {
            ValidationError error = CreateError(message, errorType, schema, value, childErrors);

            // shared cache information that could be read/populated from multiple threads
            // lock to ensure that only one thread writes known schemas
            if (Schema.KnownSchemas.Count == 0)
            {
                lock (Schema.KnownSchemas)
                {
                    if (Schema.KnownSchemas.Count == 0)
                    {
                        JSchemaDiscovery discovery = new JSchemaDiscovery(Schema.KnownSchemas, KnownSchemaState.External);
                        discovery.Discover(Schema, null);
                    }
                }
            }

            PopulateSchemaId(error);

            SchemaValidationEventHandler handler = ValidationEventHandler;
            if (handler != null)
                handler(_publicValidator, new SchemaValidationEventArgs(error));
            else
                throw JSchemaValidationException.Create(error);
        }
        protected ValidationError CreateError(string message, ErrorType errorType, JSchema schema, object value, IList<ValidationError> childErrors, IJsonLineInfo lineInfo, string path)
        {
            if (_schemaDiscovery == null)
            {
                _schemaDiscovery = new JSchemaDiscovery();
                _schemaDiscovery.Discover(Schema, null);
            }

            Uri schemaId = _schemaDiscovery.KnownSchemas.Single(s => s.Schema == schema).Id;

            ValidationError error = ValidationError.CreateValidationError(message, errorType, schema, schemaId, value, childErrors, lineInfo, path);

            return error;
        }
        public void SimpleTest()
        {
            JSchema prop = new JSchema();
            JSchema root = new JSchema
            {
                Properties =
                {
                    { "prop1", prop },
                    { "prop2", prop }
                }
            };

            JSchemaDiscovery discovery = new JSchemaDiscovery();
            discovery.Discover(root, null);

            Assert.AreEqual(2, discovery.KnownSchemas.Count);
            Assert.AreEqual(root, discovery.KnownSchemas[0].Schema);
            Assert.AreEqual(new Uri("#", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[0].Id);
            Assert.AreEqual(prop, discovery.KnownSchemas[1].Schema);
            Assert.AreEqual(new Uri("#/properties/prop1", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[1].Id);
        }
Example #5
0
        public static bool FindSchema(Action <JSchema> setSchema, JSchema schema, Uri rootSchemaId, Uri reference, JSchemaReader schemaReader, ref JSchemaDiscovery discovery)
        {
            // todo, better way to get parts from Uri
            string[] parts = reference.ToString().Split('/');

            bool resolvedSchema;

            if (parts.Length > 0 && (parts[0] == "#" || parts[0] == rootSchemaId + "#"))
            {
                schemaReader._schemaStack.Push(schema);

                parts = parts.Skip(1).ToArray();

                object current = schema;
                foreach (string part in parts)
                {
                    string unescapedPart = UnescapeReference(part);

                    if (current is JSchema)
                    {
                        JSchema s = current as JSchema;

                        schemaReader._schemaStack.Push(s);

                        switch (unescapedPart)
                        {
                        case Constants.PropertyNames.Properties:
                            current = s._properties;
                            break;

                        case Constants.PropertyNames.Items:
                            current = s._items;
                            break;

                        case Constants.PropertyNames.AdditionalProperties:
                            current = s.AdditionalProperties;
                            break;

                        case Constants.PropertyNames.AdditionalItems:
                            current = s.AdditionalItems;
                            break;

                        case Constants.PropertyNames.Not:
                            current = s.Not;
                            break;

                        case Constants.PropertyNames.OneOf:
                            current = s._oneOf;
                            break;

                        case Constants.PropertyNames.AllOf:
                            current = s._allOf;
                            break;

                        case Constants.PropertyNames.AnyOf:
                            current = s._anyOf;
                            break;

                        case Constants.PropertyNames.Enum:
                            current = s._enum;
                            break;

                        case Constants.PropertyNames.PatternProperties:
                            current = s._patternProperties;
                            break;

                        case Constants.PropertyNames.Dependencies:
                            current = s._dependencies;
                            break;

                        default:
                            JToken t;
                            s.ExtensionData.TryGetValue(unescapedPart, out t);
                            current = t;
                            break;
                        }
                    }
                    else if (current is JToken)
                    {
                        JToken resolvedToken;

                        JToken t = (JToken)current;
                        if (t is JObject)
                        {
                            resolvedToken = t[unescapedPart];
                        }
                        else if (t is JArray || t is JConstructor)
                        {
                            int index;
                            if (int.TryParse(unescapedPart, NumberStyles.None, CultureInfo.InvariantCulture, out index))
                            {
                                if (index > t.Count() || index < 0)
                                {
                                    resolvedToken = null;
                                }
                                else
                                {
                                    resolvedToken = t[index];
                                }
                            }
                            else
                            {
                                resolvedToken = null;
                            }
                        }
                        else
                        {
                            resolvedToken = null;
                        }

                        if (resolvedToken != null)
                        {
                            JSchemaAnnotation annotation = resolvedToken.Annotation <JSchemaAnnotation>();
                            if (annotation != null)
                            {
                                current = annotation.Schema;
                            }
                            else
                            {
                                current = resolvedToken;
                            }
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else if (current is IDictionary)
                    {
                        IDictionary d = (IDictionary)current;

                        current = d[unescapedPart];
                    }
                    else if (current is IList)
                    {
                        IList l = (IList)current;

                        int index;
                        if (int.TryParse(unescapedPart, NumberStyles.None, CultureInfo.InvariantCulture, out index))
                        {
                            if (index > l.Count || index < 0)
                            {
                                current = null;
                            }
                            else
                            {
                                current = l[index];
                            }
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                if (current is JToken)
                {
                    JToken t = (JToken)current;

                    JSchemaAnnotation annotation = t.Annotation <JSchemaAnnotation>();
                    if (annotation != null)
                    {
                        setSchema(annotation.Schema);
                        resolvedSchema = true;
                    }
                    else
                    {
                        JSchema inlineSchema = schemaReader.ReadInlineSchema(setSchema, t);

                        string path = reference.OriginalString;
                        if (path.StartsWith("#/", StringComparison.Ordinal))
                        {
                            path = path.Substring(2, path.Length - 2);
                        }

                        discovery.Discover(inlineSchema, rootSchemaId, path);

                        resolvedSchema = true;
                    }
                }
                else
                {
                    JSchema s = current as JSchema;
                    if (s != null)
                    {
                        setSchema(s);
                        resolvedSchema = true;

                        // schema is a reference schema and needs to be resolved
                        if (s.Reference != null)
                        {
                            schemaReader.AddDeferedSchema(setSchema, s);
                        }
                    }
                    else
                    {
                        resolvedSchema = false;
                    }
                }

                schemaReader._schemaStack.Clear();
            }
            else
            {
                discovery.Discover(schema, null);

                Uri resolvedReference = ResolveSchemaId(rootSchemaId, reference);

                // default Uri comparison ignores fragments
                // use firstordefault to handle duplicates
                KnownSchema knownSchema = discovery.KnownSchemas.FirstOrDefault(s => s.Id.OriginalString.TrimEnd('#') == resolvedReference.OriginalString.TrimEnd('#'));

                if (knownSchema != null)
                {
                    resolvedSchema = true;
                    setSchema(knownSchema.Schema);
                }
                else
                {
                    int hashIndex = resolvedReference.OriginalString.IndexOf('#');
                    if (hashIndex != -1)
                    {
                        Uri path     = new Uri(resolvedReference.OriginalString.Substring(0, hashIndex), UriKind.RelativeOrAbsolute);
                        Uri fragment = new Uri(resolvedReference.OriginalString.Substring(hashIndex), UriKind.RelativeOrAbsolute);

                        // default Uri comparison ignores fragments
                        // there could be duplicated ids. use FirstOrDefault to get first schema with an id
                        knownSchema = discovery.KnownSchemas.FirstOrDefault(s => s.Id.OriginalString.TrimEnd('#') == path.OriginalString);

                        if (knownSchema != null)
                        {
                            // don't attempt to find a schema in the same schema again
                            // avoids stackoverflow
                            if (knownSchema.Schema != schema ||
                                !UriComparer.Instance.Equals(rootSchemaId, path) ||
                                !UriComparer.Instance.Equals(reference, fragment))
                            {
                                resolvedSchema = FindSchema(setSchema, knownSchema.Schema, path, fragment, schemaReader, ref discovery);
                            }
                            else
                            {
                                resolvedSchema = false;
                            }
                        }
                        else
                        {
                            resolvedSchema = false;
                        }
                    }
                    else
                    {
                        resolvedSchema = false;
                    }
                }
            }
            return(resolvedSchema);
        }
        public void RootId_NestedId()
        {
            JSchema prop1 = new JSchema
            {
                Id = new Uri("test.json/", UriKind.RelativeOrAbsolute),
                Items =
                {
                    new JSchema(),
                    new JSchema
                    {
                        Id = new Uri("#fragmentItem2", UriKind.RelativeOrAbsolute),
                        Items =
                        {
                            new JSchema(),
                            new JSchema { Id = new Uri("#fragmentItem2Item2", UriKind.RelativeOrAbsolute) },
                            new JSchema { Id = new Uri("file.json", UriKind.RelativeOrAbsolute) },
                            new JSchema { Id = new Uri("/file1.json", UriKind.RelativeOrAbsolute) }
                        }
                    }
                }
            };
            JSchema prop2 = new JSchema
            {
                Id = new Uri("#fragment", UriKind.RelativeOrAbsolute),
                Not = new JSchema()
            };
            JSchema root = new JSchema
            {
                Id = new Uri("http://localhost/", UriKind.RelativeOrAbsolute),
                Properties =
                {
                    { "prop1", prop1 },
                    { "prop2", prop2 }
                },
                ExtensionData =
                {
                    {
                        "definitions",
                        new JObject
                        {
                            { "def1", new JSchema() },
                            { "def2", new JSchema { Id = new Uri("def2.json", UriKind.RelativeOrAbsolute) } },
                            {
                                "defn",
                                new JArray
                                {
                                    new JValue(5),
                                    new JSchema()
                                }
                            }
                        }
                    }
                }
            };

            JSchemaDiscovery discovery = new JSchemaDiscovery();
            discovery.Discover(root, null);

            int i = 0;

            Assert.AreEqual(new Uri("http://localhost/#", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root, discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/#/definitions/def1", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual((JSchema)root.ExtensionData["definitions"]["def1"], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/def2.json", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual((JSchema)root.ExtensionData["definitions"]["def2"], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/#/definitions/defn/1", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual((JSchema)root.ExtensionData["definitions"]["defn"][1], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/test.json/", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop1"], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/test.json/#/items/0", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop1"].Items[0], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/test.json/#fragmentItem2", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop1"].Items[1], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/test.json/#fragmentItem2/items/0", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop1"].Items[1].Items[0], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/test.json/#fragmentItem2Item2", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop1"].Items[1].Items[1], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/test.json/file.json", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop1"].Items[1].Items[2], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/file1.json", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop1"].Items[1].Items[3], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/#fragment", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop2"], discovery.KnownSchemas[i++].Schema);

            Assert.AreEqual(new Uri("http://localhost/#fragment/not", UriKind.RelativeOrAbsolute), discovery.KnownSchemas[i].Id);
            Assert.AreEqual(root.Properties["prop2"].Not, discovery.KnownSchemas[i++].Schema);
        }
        public void Draft4Example()
        {
            JSchema schema1 = new JSchema
            {
                Id = new Uri("#foo", UriKind.RelativeOrAbsolute),
                Title = "schema1"
            };
            JSchema schema2 = new JSchema
            {
                Id = new Uri("otherschema.json", UriKind.RelativeOrAbsolute),
                Title = "schema2",
                ExtensionData =
                {
                    {
                        "nested",
                        new JSchema
                        {
                            Title = "nested",
                            Id = new Uri("#bar", UriKind.RelativeOrAbsolute)
                        }
                    },
                    {
                        "alsonested",
                        new JSchema
                        {
                            Title = "alsonested",
                            Id = new Uri("t/inner.json#a", UriKind.RelativeOrAbsolute),
                            ExtensionData =
                            {
                                {
                                    "nestedmore",
                                    new JSchema { Title = "nestedmore" }
                                }
                            }
                        }
                    }
                }
            };
            JSchema schema3 = new JSchema
            {
                Title = "schema3",
                Id = new Uri("some://where.else/completely#", UriKind.RelativeOrAbsolute)
            };

            JSchema root = new JSchema
            {
                Id = new Uri("http://x.y.z/rootschema.json#", UriKind.RelativeOrAbsolute),
                ExtensionData =
                {
                    { "schema1", schema1 },
                    { "schema2", schema2 },
                    { "schema3", schema3 }
                }
            };

            JSchemaDiscovery discovery = new JSchemaDiscovery();
            discovery.Discover(root, null);

            Assert.AreEqual(7, discovery.KnownSchemas.Count);
            Assert.AreEqual("http://x.y.z/rootschema.json#", discovery.KnownSchemas[0].Id.ToString());
            Assert.AreEqual("http://x.y.z/rootschema.json#foo", discovery.KnownSchemas[1].Id.ToString());
            Assert.AreEqual("http://x.y.z/otherschema.json", discovery.KnownSchemas[2].Id.ToString());
            Assert.AreEqual("http://x.y.z/otherschema.json#bar", discovery.KnownSchemas[3].Id.ToString());
            Assert.AreEqual("http://x.y.z/t/inner.json#a", discovery.KnownSchemas[4].Id.ToString());
            Assert.AreEqual("http://x.y.z/t/inner.json#/nestedmore", discovery.KnownSchemas[5].Id.ToString());
            Assert.AreEqual("some://where.else/completely#", discovery.KnownSchemas[6].Id.ToString());
        }
        public static bool FindSchema(Action <JSchema> setSchema, JSchema schema, Uri rootSchemaId, Uri reference, JSchemaReader schemaReader)
        {
            // todo, better way to get parts from Uri
            string[] parts = reference.ToString().Split('/');

            bool resolvedSchema;

            if (parts.Length > 0 && (parts[0] == "#" || parts[0] == rootSchemaId + "#"))
            {
                schemaReader._schemaStack.Push(schema);

                parts = parts.Skip(1).ToArray();

                object current = schema;
                foreach (string part in parts)
                {
                    string unescapedPart = UnescapeReference(part);

                    if (current is JSchema)
                    {
                        JSchema s = current as JSchema;

                        schemaReader._schemaStack.Push(s);

                        switch (unescapedPart)
                        {
                        case Constants.PropertyNames.Properties:
                            current = s._properties;
                            break;

                        case Constants.PropertyNames.Items:
                            current = s._items;
                            break;

                        case Constants.PropertyNames.AdditionalProperties:
                            current = s.AdditionalProperties;
                            break;

                        case Constants.PropertyNames.AdditionalItems:
                            current = s.AdditionalItems;
                            break;

                        case Constants.PropertyNames.Not:
                            current = s.Not;
                            break;

                        case Constants.PropertyNames.OneOf:
                            current = s._oneOf;
                            break;

                        case Constants.PropertyNames.AllOf:
                            current = s._allOf;
                            break;

                        case Constants.PropertyNames.AnyOf:
                            current = s._anyOf;
                            break;

                        case Constants.PropertyNames.Enum:
                            current = s._enum;
                            break;

                        case Constants.PropertyNames.PatternProperties:
                            current = s._patternProperties;
                            break;

                        default:
                            JToken t;
                            s.ExtensionData.TryGetValue(unescapedPart, out t);
                            current = t;
                            break;
                        }
                    }
                    else if (current is JToken)
                    {
                        JToken resolvedToken;

                        JToken t = (JToken)current;
                        if (t is JObject)
                        {
                            resolvedToken = t[unescapedPart];
                        }
                        else if (t is JArray || t is JConstructor)
                        {
                            int index;
                            if (int.TryParse(unescapedPart, NumberStyles.None, CultureInfo.InvariantCulture, out index))
                            {
                                if (index > t.Count() || index < 0)
                                {
                                    resolvedToken = null;
                                }
                                else
                                {
                                    resolvedToken = t[index];
                                }
                            }
                            else
                            {
                                resolvedToken = null;
                            }
                        }
                        else
                        {
                            resolvedToken = null;
                        }

                        if (resolvedToken != null)
                        {
                            JSchemaAnnotation annotation = resolvedToken.Annotation <JSchemaAnnotation>();
                            if (annotation != null)
                            {
                                current = annotation.Schema;
                            }
                            else
                            {
                                current = resolvedToken;
                            }
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else if (current is IDictionary)
                    {
                        IDictionary d = (IDictionary)current;

                        current = d[unescapedPart];
                    }
                    else if (current is IList)
                    {
                        IList l = (IList)current;

                        int index;
                        if (int.TryParse(unescapedPart, NumberStyles.None, CultureInfo.InvariantCulture, out index))
                        {
                            if (index > l.Count || index < 0)
                            {
                                current = null;
                            }
                            else
                            {
                                current = l[index];
                            }
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                if (current is JToken)
                {
                    JToken t = (JToken)current;

                    JSchemaAnnotation annotation = t.Annotation <JSchemaAnnotation>();
                    if (annotation != null)
                    {
                        setSchema(annotation.Schema);
                        resolvedSchema = true;
                    }
                    else
                    {
                        schemaReader.ReadInlineSchema(setSchema, t);
                        resolvedSchema = true;
                    }
                }
                else
                {
                    var s = current as JSchema;
                    if (s != null)
                    {
                        setSchema(s);
                        resolvedSchema = true;
                    }
                    else
                    {
                        resolvedSchema = false;
                    }
                }

                schemaReader._schemaStack.Clear();
            }
            else
            {
                JSchemaDiscovery discovery = new JSchemaDiscovery();
                discovery.Discover(schema, null);

                Uri resolvedReference = ResolveSchemaId(rootSchemaId, reference);

                // default Uri comparison ignores fragments
                KnownSchema knownSchema = discovery.KnownSchemas.SingleOrDefault(s => s.Id.OriginalString.TrimEnd('#') == resolvedReference.OriginalString.TrimEnd('#'));

                if (knownSchema != null)
                {
                    resolvedSchema = true;
                    setSchema(knownSchema.Schema);
                }
                else
                {
                    int hashIndex = resolvedReference.OriginalString.IndexOf('#');
                    if (hashIndex != -1)
                    {
                        Uri path     = new Uri(resolvedReference.OriginalString.Substring(0, hashIndex), UriKind.RelativeOrAbsolute);
                        Uri fragment = new Uri(resolvedReference.OriginalString.Substring(hashIndex), UriKind.RelativeOrAbsolute);

                        // default Uri comparison ignores fragments
                        knownSchema = discovery.KnownSchemas.SingleOrDefault(s => s.Id.OriginalString.TrimEnd('#') == path.OriginalString);

                        if (knownSchema != null)
                        {
                            resolvedSchema = FindSchema(setSchema, knownSchema.Schema, path, fragment, schemaReader);
                        }
                        else
                        {
                            resolvedSchema = false;
                        }
                    }
                    else
                    {
                        resolvedSchema = false;
                    }
                }
            }
            return(resolvedSchema);
        }
        public static bool FindSchema(Action<JSchema> setSchema, JSchema schema, Uri rootSchemaId, Uri reference, JSchemaReader schemaReader)
        {
            // todo, better way to get parts from Uri
            string[] parts = reference.ToString().Split('/');

            bool resolvedSchema;

            if (parts.Length > 0 && (parts[0] == "#" || parts[0] == rootSchemaId + "#"))
            {
                schemaReader._schemaStack.Push(schema);

                parts = parts.Skip(1).ToArray();

                object current = schema;
                foreach (string part in parts)
                {
                    string unescapedPart = UnescapeReference(part);

                    if (current is JSchema)
                    {
                        JSchema s = current as JSchema;

                        schemaReader._schemaStack.Push(s);

                        switch (unescapedPart)
                        {
                            case Constants.PropertyNames.Properties:
                                current = s._properties;
                                break;
                            case Constants.PropertyNames.Items:
                                current = s._items;
                                break;
                            case Constants.PropertyNames.AdditionalProperties:
                                current = s.AdditionalProperties;
                                break;
                            case Constants.PropertyNames.AdditionalItems:
                                current = s.AdditionalItems;
                                break;
                            case Constants.PropertyNames.Not:
                                current = s.Not;
                                break;
                            case Constants.PropertyNames.OneOf:
                                current = s._oneOf;
                                break;
                            case Constants.PropertyNames.AllOf:
                                current = s._allOf;
                                break;
                            case Constants.PropertyNames.AnyOf:
                                current = s._anyOf;
                                break;
                            case Constants.PropertyNames.Enum:
                                current = s._enum;
                                break;
                            case Constants.PropertyNames.PatternProperties:
                                current = s._patternProperties;
                                break;
                            case Constants.PropertyNames.Dependencies:
                                current = s._dependencies;
                                break;
                            default:
                                JToken t;
                                s.ExtensionData.TryGetValue(unescapedPart, out t);
                                current = t;
                                break;
                        }
                    }
                    else if (current is JToken)
                    {
                        JToken resolvedToken;

                        JToken t = (JToken)current;
                        if (t is JObject)
                        {
                            resolvedToken = t[unescapedPart];
                        }
                        else if (t is JArray || t is JConstructor)
                        {
                            int index;
                            if (int.TryParse(unescapedPart, NumberStyles.None, CultureInfo.InvariantCulture, out index))
                            {
                                if (index > t.Count() || index < 0)
                                    resolvedToken = null;
                                else
                                    resolvedToken = t[index];
                            }
                            else
                            {
                                resolvedToken = null;
                            }
                        }
                        else
                        {
                            resolvedToken = null;
                        }

                        if (resolvedToken != null)
                        {
                            JSchemaAnnotation annotation = resolvedToken.Annotation<JSchemaAnnotation>();
                            if (annotation != null)
                                current = annotation.Schema;
                            else
                                current = resolvedToken;
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else if (current is IDictionary)
                    {
                        IDictionary d = (IDictionary)current;

                        current = d[unescapedPart];
                    }
                    else if (current is IList)
                    {
                        IList l = (IList)current;

                        int index;
                        if (int.TryParse(unescapedPart, NumberStyles.None, CultureInfo.InvariantCulture, out index))
                        {
                            if (index > l.Count || index < 0)
                                current = null;
                            else
                                current = l[index];
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                if (current is JToken)
                {
                    JToken t = (JToken)current;

                    JSchemaAnnotation annotation = t.Annotation<JSchemaAnnotation>();
                    if (annotation != null)
                    {
                        setSchema(annotation.Schema);
                        resolvedSchema = true;
                    }
                    else
                    {
                        schemaReader.ReadInlineSchema(setSchema, t);
                        resolvedSchema = true;
                    }
                }
                else
                {
                    JSchema s = current as JSchema;
                    if (s != null)
                    {
                        setSchema(s);
                        resolvedSchema = true;

                        // schema is a reference schema and needs to be resolved
                        if (s.Reference != null)
                            schemaReader.AddDeferedSchema(setSchema, s);
                    }
                    else
                    {
                        resolvedSchema = false;
                    }
                }

                schemaReader._schemaStack.Clear();
            }
            else
            {
                JSchemaDiscovery discovery = new JSchemaDiscovery();
                discovery.Discover(schema, null);

                Uri resolvedReference = ResolveSchemaId(rootSchemaId, reference);

                // default Uri comparison ignores fragments
                KnownSchema knownSchema = discovery.KnownSchemas.SingleOrDefault(s => s.Id.OriginalString.TrimEnd('#') == resolvedReference.OriginalString.TrimEnd('#'));

                if (knownSchema != null)
                {
                    resolvedSchema = true;
                    setSchema(knownSchema.Schema);
                }
                else
                {
                    int hashIndex = resolvedReference.OriginalString.IndexOf('#');
                    if (hashIndex != -1)
                    {
                        Uri path = new Uri(resolvedReference.OriginalString.Substring(0, hashIndex), UriKind.RelativeOrAbsolute);
                        Uri fragment = new Uri(resolvedReference.OriginalString.Substring(hashIndex), UriKind.RelativeOrAbsolute);

                        // default Uri comparison ignores fragments
                        knownSchema = discovery.KnownSchemas.SingleOrDefault(s => s.Id.OriginalString.TrimEnd('#') == path.OriginalString);

                        if (knownSchema != null)
                        {
                            // don't attempt to find a schema in the same schema again
                            // avoids stackoverflow
                            if (knownSchema.Schema != schema
                                || !UriComparer.Instance.Equals(rootSchemaId, path)
                                || !UriComparer.Instance.Equals(reference, fragment))
                            {
                                resolvedSchema = FindSchema(setSchema, knownSchema.Schema, path, fragment, schemaReader);
                            }
                            else
                            {
                                resolvedSchema = false;
                            }
                        }
                        else
                        {
                            resolvedSchema = false;
                        }
                    }
                    else
                    {
                        resolvedSchema = false;
                    }
                }
            }
            return resolvedSchema;
        }
        public void DuplicateIds()
        {
            JSchema root = new JSchema
            {
                Properties =
                {
                    {
                        "prop1", new JSchema
                        {
                            Id = new Uri("duplicate", UriKind.RelativeOrAbsolute),
                            Properties =
                            {
                                { "test", new JSchema() }
                            }
                        }
                    },
                    {
                        "prop2", new JSchema
                        {
                            Id = new Uri("duplicate", UriKind.RelativeOrAbsolute),
                            Properties =
                            {
                                { "test", new JSchema() }
                            }
                        }
                    }
                }
            };

            JSchemaDiscovery discovery = new JSchemaDiscovery();
            discovery.Discover(root, null);

            Assert.AreEqual("#", discovery.KnownSchemas[0].Id.OriginalString);
            Assert.AreEqual("duplicate", discovery.KnownSchemas[1].Id.OriginalString);
            Assert.AreEqual("duplicate#/properties/test", discovery.KnownSchemas[2].Id.OriginalString);
            Assert.AreEqual("duplicate", discovery.KnownSchemas[3].Id.OriginalString);
            Assert.AreEqual("duplicate#/properties/test", discovery.KnownSchemas[4].Id.OriginalString);
        }
        public void MultipleNestedPaths()
        {
            string schemaJson = @"{
    ""$schema"": ""http://json-schema.org/draft-04/schema#"",
    ""type"": ""object"",
    ""title"": ""Quotes Configurator Schema"",
    ""description"": ""Defines the constraints for an acceptable JSON object required to produce a package"",
    ""properties"": {
       ""working_directory"" : { ""$ref"": ""#/definitions/working_directory"" },
       ""environment"" : { ""$ref"": ""#/definitions/environment"" },
        ""timezone"" : {
          ""description"": ""Timezone in in which processes are scheduled, does control the timezone in which they run"",
          ""type"": ""string""
       },
       ""cleanup_directories"" : {
          ""description"": ""Directories (and corresponding file types) which will be cleaned twice daily"",
          ""type"": ""object""
       },
       ""holiday_key"" : {
          ""description"": ""Holiday key which your package will follow"",
          ""type"": ""string""
       },
       ""cpu_alerting_threshold"" : {
          ""description"": ""Alerting threshold for high CPU usage"",
          ""type"": ""integer"",
          ""minimum"": 75,
          ""maximum"" : 250,
          ""multipleOf"" : 1
       },
       ""processes"" : { ""$ref"": ""#/definitions/processes"" },
       ""schedule"" : { ""$ref"": ""#/definitions/schedule"" }
    },
    ""additionalProperties"": false,
    ""required"": [
        ""working_directory"",
        ""timezone"",
        ""environment"",
        ""cleanup_directories"",
        ""processes"",
        ""schedule""
    ],
    ""definitions"": {
        ""nonEmptyWord"" : {
           ""type"" : ""string"",
           ""pattern"" : ""(^\\S+$)""
        },
        ""nonEmptyString"" : {
           ""type"" : ""string"",
           ""pattern"": ""(^\\S+(\\S|\\s)+$)""
        },
        ""dayShorthand"" : {
           ""type"" : ""string"",
           ""enum"" : [ ""Sun"", ""Mon"", ""Tue"", ""Wed"", ""Thu"", ""Fri"", ""Sat"" ]
        },
        ""twentyFourHourTime"" : {
           ""type"" : ""integer"",
           ""minimum"": 0,
           ""maximum"" : 235959,
           ""multipleOf"" : 1
        },
        ""environment"" : {
           ""description"": ""Global environment settings for each of the processes"",
           ""type"": ""object""
        },
        ""working_directory"" : {
           ""description"": ""Working directory for each process. Usually either root directory of your package or its etc/ directory"",
           ""type"": ""string"",
           ""pattern"": ""(^@\\w+@$)""
        },
        ""process"": {
           ""id"": ""process"",
           ""type"" : ""object"",
           ""properties"": {
              ""working_directory"" : { ""$ref"": ""../#/definitions/working_directory"" },
              ""environment"" : { ""$ref"": ""../#/definitions/environment"" },
              ""name"" : { ""$ref"": ""../#/definitions/nonEmptyWord"" },
              ""description"" : { ""$ref"": ""../#/definitions/nonEmptyString"" },
              ""ulimit"" : {
                 ""type"" : ""integer"",
                 ""multipleOf"": 256,
                 ""minimum"" : 256
              },
              ""alert_on_downtime"" : { ""type"" : ""boolean"" },
              ""starts_on_holiday"" : { ""type"" : ""boolean"" },
              ""check_for_completion"" : { ""type"" : ""boolean"" },
              ""arguments"" : {
                 ""type"" : ""array"",
                 ""items"" : { ""$ref"": ""../#/definitions/nonEmptyString"" },
                 ""minItems"": 1
              },
              ""stats_store_groups"" : {
                 ""type"" : ""array"",
                 ""items"" : { ""$ref"": ""../#/definitions/nonEmptyWord"" },
                 ""uniqueItems"" : true
              }
           },
           ""patternProperties"": {

              ""(^binary$|^module$|^external$|^bash$|^python$|^perl$)"" : { ""$ref"": ""../#/definitions/nonEmptyWord"" },
           },
           ""additionalProperties"": false,
           ""minProperties"": 5,
           ""maxProperties"": 12,
           ""allOf"" : [
             { ""required"": [ ""name"", ""ulimit"", ""alert_on_downtime"", ""starts_on_holiday"" ] },
             { ""oneOf"": [
               { ""required"" :  [ ""binary"" ] },
               { ""required"" :  [ ""module"" ] },
               { ""required"" :  [ ""external"" ] },
               { ""required"" :  [ ""bash"" ] },
               { ""required"" :  [ ""python"" ] },
               { ""required"" :  [ ""perl"" ] }
                ]
             }
           ]
        },
        ""processes"": {
          ""description"": ""All processes that are allowed to be run"",
          ""type"" : ""array"",
          ""items"" : { ""$ref"": ""#/definitions/process"" }
        },
        ""task"": {
          ""id"" : ""task"",
          ""type"" : ""object"",
          ""properties"" : {
             ""task_name"" : { ""$ref"": ""../#/definitions/nonEmptyWord"" },
             ""processes"" : {
                ""type"" : ""array""
             },
             ""runs_on""   : {
                ""type"" : ""array"",
                ""items"" : { ""$ref"": ""../#/definitions/dayShorthand"" },
                ""uniqueItems"" : true
             }
          },
          ""patternProperties"": {
             ""(^start_at$|^end_at$)"" : { ""$ref"": ""../#/definitions/twentyFourHourTime"" },
          },
          ""allOf"" : [
             { ""required"": [ ""task_name"", ""processes"", ""runs_on"" ] },
             { ""oneOf"" : [
                   {
                      ""anyOf"": [
                         { ""required"" :  [ ""start_at"" ] }
                      ]
                   },
                   {
                      ""anyOf"": [
                         { ""required"" :  [ ""end_at"" ] }
                      ]
                   }
                ]
             },
           ],
          ""additionalProperties"": false
        },
        ""schedule"": {
           ""description"": ""Separate tasks which consist of one or more processes and will run under your package"",
           ""type"" : ""array"",
           ""items"" : { ""$ref"": ""#/definitions/task"" }
        }
    }
}";

            JSchema schema = JSchema.Parse(schemaJson);

            JSchemaDiscovery discovery = new JSchemaDiscovery();
            discovery.Discover(schema, null);

            //for (int i = 0; i < discovery.KnownSchemas.Count; i++)
            //{
            //    KnownSchema knownSchema = discovery.KnownSchemas[i];

            //    Console.WriteLine(string.Format(@"Assert.AreEqual(""{0}"", discovery.KnownSchemas[{1}].Id.OriginalString);", knownSchema.Id, i));
            //}

            Assert.AreEqual("#", discovery.KnownSchemas[0].Id.OriginalString);
            Assert.AreEqual("#/definitions/nonEmptyWord", discovery.KnownSchemas[1].Id.OriginalString);
            Assert.AreEqual("#/definitions/nonEmptyString", discovery.KnownSchemas[2].Id.OriginalString);
            Assert.AreEqual("#/definitions/dayShorthand", discovery.KnownSchemas[3].Id.OriginalString);
            Assert.AreEqual("#/definitions/twentyFourHourTime", discovery.KnownSchemas[4].Id.OriginalString);
            Assert.AreEqual("#/definitions/environment", discovery.KnownSchemas[5].Id.OriginalString);
            Assert.AreEqual("#/definitions/working_directory", discovery.KnownSchemas[6].Id.OriginalString);
            Assert.AreEqual("process", discovery.KnownSchemas[7].Id.OriginalString);
            Assert.AreEqual("process#/properties/ulimit", discovery.KnownSchemas[8].Id.OriginalString);
            Assert.AreEqual("process#/properties/alert_on_downtime", discovery.KnownSchemas[9].Id.OriginalString);
            Assert.AreEqual("process#/properties/starts_on_holiday", discovery.KnownSchemas[10].Id.OriginalString);
            Assert.AreEqual("process#/properties/check_for_completion", discovery.KnownSchemas[11].Id.OriginalString);
            Assert.AreEqual("process#/properties/arguments", discovery.KnownSchemas[12].Id.OriginalString);
            Assert.AreEqual("process#/properties/stats_store_groups", discovery.KnownSchemas[13].Id.OriginalString);
            Assert.AreEqual("process#/allOf/0", discovery.KnownSchemas[14].Id.OriginalString);
            Assert.AreEqual("process#/allOf/1", discovery.KnownSchemas[15].Id.OriginalString);
            Assert.AreEqual("process#/allOf/1/oneOf/0", discovery.KnownSchemas[16].Id.OriginalString);
            Assert.AreEqual("process#/allOf/1/oneOf/1", discovery.KnownSchemas[17].Id.OriginalString);
            Assert.AreEqual("process#/allOf/1/oneOf/2", discovery.KnownSchemas[18].Id.OriginalString);
            Assert.AreEqual("process#/allOf/1/oneOf/3", discovery.KnownSchemas[19].Id.OriginalString);
            Assert.AreEqual("process#/allOf/1/oneOf/4", discovery.KnownSchemas[20].Id.OriginalString);
            Assert.AreEqual("process#/allOf/1/oneOf/5", discovery.KnownSchemas[21].Id.OriginalString);
            Assert.AreEqual("#/definitions/processes", discovery.KnownSchemas[22].Id.OriginalString);
            Assert.AreEqual("task", discovery.KnownSchemas[23].Id.OriginalString);
            Assert.AreEqual("task#/properties/processes", discovery.KnownSchemas[24].Id.OriginalString);
            Assert.AreEqual("task#/properties/runs_on", discovery.KnownSchemas[25].Id.OriginalString);
            Assert.AreEqual("task#/allOf/0", discovery.KnownSchemas[26].Id.OriginalString);
            Assert.AreEqual("task#/allOf/1", discovery.KnownSchemas[27].Id.OriginalString);
            Assert.AreEqual("task#/allOf/1/oneOf/0", discovery.KnownSchemas[28].Id.OriginalString);
            Assert.AreEqual("task#/allOf/1/oneOf/0/anyOf/0", discovery.KnownSchemas[29].Id.OriginalString);
            Assert.AreEqual("task#/allOf/1/oneOf/1", discovery.KnownSchemas[30].Id.OriginalString);
            Assert.AreEqual("task#/allOf/1/oneOf/1/anyOf/0", discovery.KnownSchemas[31].Id.OriginalString);
            Assert.AreEqual("#/definitions/schedule", discovery.KnownSchemas[32].Id.OriginalString);
            Assert.AreEqual("#/properties/timezone", discovery.KnownSchemas[33].Id.OriginalString);
            Assert.AreEqual("#/properties/cleanup_directories", discovery.KnownSchemas[34].Id.OriginalString);
            Assert.AreEqual("#/properties/holiday_key", discovery.KnownSchemas[35].Id.OriginalString);
            Assert.AreEqual("#/properties/cpu_alerting_threshold", discovery.KnownSchemas[36].Id.OriginalString);
        }
        public void ComplexPath()
        {
            string path = TestHelpers.ResolveFilePath(@"resources\schemas\custom\validator1.json");

            string schemaJson = File.ReadAllText(path);
            JSchema schema = JSchema.Parse(schemaJson);

            JSchemaDiscovery discovery = new JSchemaDiscovery();
            discovery.Discover(schema, null);

            Console.WriteLine(discovery.KnownSchemas[3].Id.OriginalString);

            // ensure the path does not contain multiple #'s
            Assert.AreEqual("http://www.example.org/IntegralLifeProduct#/definitions/ProductType/allOf/0", discovery.KnownSchemas[3].Id.OriginalString);
        }
        public static bool FindSchema(Action <JSchema> setSchema, JSchema schema, Uri rootSchemaId, Uri reference, JSchemaReader schemaReader, ref JSchemaDiscovery discovery)
        {
            // todo, better way to get parts from Uri
            string[] parts = reference.ToString().Split('/');

            bool resolvedSchema;

            if (parts.Length > 0 && (parts[0] == "#" || parts[0] == rootSchemaId + "#"))
            {
                schemaReader._schemaStack.Add(schema);

                JSchema parent  = schema;
                object  current = schema;
                for (int i = 1; i != parts.Length; ++i)
                {
                    string unescapedPart = UnescapeReference(parts[i]);

                    JSchema s = current as JSchema;
                    if (s != null)
                    {
                        schemaReader._schemaStack.Add(s);

                        parent  = s;
                        current = GetCurrentFromSchema(s, unescapedPart);
                    }
                    else if (current is JToken)
                    {
                        current = GetCurrentFromToken((JToken)current, unescapedPart);
                    }
                    else if (current is IDictionary <string, JSchema> )
                    {
                        IDictionary <string, JSchema> d = (IDictionary <string, JSchema>)current;

                        JSchema temp;
                        d.TryGetValue(unescapedPart, out temp);
                        current = temp;
                    }
                    else if (current is IList <JSchema> )
                    {
                        IList <JSchema> l = (IList <JSchema>)current;
                        int             index;

                        // if the schema collection is items then implicitly get first item if there is no position validation
                        if (ReferenceEquals(parent._items, l) && !parent.ItemsPositionValidation)
                        {
                            if (l.Count > 0)
                            {
                                current = GetCurrentFromSchema(l[0], unescapedPart);
                            }
                            else
                            {
                                current = null;
                            }
                        }
                        else if (int.TryParse(unescapedPart, NumberStyles.None, CultureInfo.InvariantCulture, out index))
                        {
                            if (index > l.Count || index < 0)
                            {
                                current = null;
                            }
                            else
                            {
                                current = l[index];
                            }
                        }
                        else
                        {
                            current = null;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                JToken t = current as JToken;
                if (t != null)
                {
                    JSchemaAnnotation annotation = t.Annotation <JSchemaAnnotation>();
                    if (annotation != null)
                    {
                        setSchema(annotation.Schema);
                        resolvedSchema = true;
                    }
                    else
                    {
                        JSchema inlineSchema = schemaReader.ReadInlineSchema(setSchema, t);

                        string path = reference.OriginalString;
                        if (path.StartsWith("#/", StringComparison.Ordinal))
                        {
                            path = path.Substring(2, path.Length - 2);
                        }

                        discovery.Discover(inlineSchema, rootSchemaId, path);

                        resolvedSchema = true;
                    }
                }
                else
                {
                    JSchema s = current as JSchema;
                    if (s != null)
                    {
                        setSchema(s);
                        resolvedSchema = true;

                        // schema is a reference schema and needs to be resolved
                        if (s.Reference != null)
                        {
                            schemaReader.AddDeferedSchema(null, setSchema, s);
                        }
                    }
                    else
                    {
                        resolvedSchema = false;
                    }
                }

                schemaReader._schemaStack.Clear();
            }
            else
            {
                discovery.Discover(schema, null);

                Uri resolvedReference = ResolveSchemaId(rootSchemaId, reference);

                // use firstordefault to handle duplicates
                KnownSchema knownSchema = discovery.KnownSchemas.FirstOrDefault(s => UriComparer.Instance.Equals(s.Id, resolvedReference));

                if (knownSchema != null)
                {
                    resolvedSchema = true;
                    setSchema(knownSchema.Schema);
                }
                else
                {
                    int hashIndex = resolvedReference.OriginalString.IndexOf('#');
                    if (hashIndex != -1)
                    {
                        Uri path     = new Uri(resolvedReference.OriginalString.Substring(0, hashIndex), UriKind.RelativeOrAbsolute);
                        Uri fragment = new Uri(resolvedReference.OriginalString.Substring(hashIndex), UriKind.RelativeOrAbsolute);

                        // there could be duplicated ids. use FirstOrDefault to get first schema with an id
                        knownSchema = discovery.KnownSchemas.FirstOrDefault(s => UriComparer.Instance.Equals(s.Id, path));

                        if (knownSchema != null)
                        {
                            // don't attempt to find a schema in the same schema again
                            // avoids stackoverflow
                            if (knownSchema.Schema != schema ||
                                !UriComparer.Instance.Equals(rootSchemaId, path) ||
                                !UriComparer.Instance.Equals(reference, fragment))
                            {
                                resolvedSchema = FindSchema(setSchema, knownSchema.Schema, path, fragment, schemaReader, ref discovery);
                            }
                            else
                            {
                                resolvedSchema = false;
                            }
                        }
                        else
                        {
                            resolvedSchema = false;
                        }
                    }
                    else
                    {
                        // special case
                        // look in the root schema's definitions for a definition with the same property name and id as reference
                        JToken definitions;
                        if (schema.ExtensionData.TryGetValue(Constants.PropertyNames.Definitions, out definitions))
                        {
                            JObject definitionsObject = definitions as JObject;
                            if (definitionsObject != null)
                            {
                                JProperty matchingProperty = definitionsObject.Properties().FirstOrDefault(p => TryCompare(p.Name, resolvedReference));

                                JObject o = matchingProperty?.Value as JObject;
                                if (o != null && TryCompare((string)o["id"], resolvedReference))
                                {
                                    JSchema inlineSchema = schemaReader.ReadInlineSchema(setSchema, o);

                                    discovery.Discover(inlineSchema, rootSchemaId, Constants.PropertyNames.Definitions + "/" + resolvedReference.OriginalString);

                                    resolvedSchema = true;
                                }
                                else
                                {
                                    resolvedSchema = false;
                                }
                            }
                            else
                            {
                                resolvedSchema = false;
                            }
                        }
                        else
                        {
                            resolvedSchema = false;
                        }
                    }
                }
            }

            return(resolvedSchema);
        }