Skip to content

Commit

Permalink
-Fixed resolving complex schema references
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesNK committed Feb 7, 2015
1 parent ef2b4a5 commit 6c43400
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ public void GeoJson()
Assert.IsTrue(isValid);
}

[Test]
public void ChromeManifest()
{
string schemaJson = TestHelpers.OpenFileText(@"resources\schemas\chrome-manifest.json");
JSchema chromeManifestSchema = JSchema.Parse(schemaJson);

Assert.AreEqual("JSON schema for Google Chrome extension manifest files", chromeManifestSchema.Title);

Console.WriteLine(chromeManifestSchema.ToString());
}

[Test]
public void Simple()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,27 @@ public void SerializeError()

string json = JsonConvert.SerializeObject(error, Formatting.Indented);

Console.WriteLine(json);
Assert.AreEqual(@"{
""Message"": ""A message!"",
""LineNumber"": 11,
""LinePosition"": 5,
""Path"": ""sdf.sdf"",
""Value"": ""A value!"",
""SchemaId"": ""test.xml"",
""ErrorType"": ""minLength"",
""ChildErrors"": [
{
""Message"": ""Child message!"",
""LineNumber"": 0,
""LinePosition"": 0,
""Path"": null,
""Value"": null,
""SchemaId"": null,
""ErrorType"": ""none"",
""ChildErrors"": []
}
]
}", json);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@
<None Include="Resources\Schemas\schema-catalog.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Schemas\schema-draft-v3.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Resources\Schemas\schema-draft-v4.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
{
"$schema": "http://json-schema.org/draft-03/schema#",
"id": "http://json-schema.org/draft-03/schema#",
"type": "object",
"properties": {
"type": {
"type": [ "string", "array" ],
"items": {
"type": [ "string", { "$ref": "#" } ]
},
"uniqueItems": true,
"default": "any"
},
"properties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"patternProperties": {
"type": "object",
"additionalProperties": { "$ref": "#" },
"default": {}
},
"additionalProperties": {
"type": [ { "$ref": "#" }, "boolean" ],
"default": {}
},
"items": {
"type": [ { "$ref": "#" }, "array" ],
"items": { "$ref": "#" },
"default": {}
},
"additionalItems": {
"type": [ { "$ref": "#" }, "boolean" ],
"default": {}
},
"required": {
"type": "boolean",
"default": false
},
"dependencies": {
"type": "object",
"additionalProperties": {
"type": [ "string", "array", { "$ref": "#" } ],
"items": {
"type": "string"
}
},
"default": {}
},
"minimum": {
"type": "number"
},
"maximum": {
"type": "number"
},
"exclusiveMinimum": {
"type": "boolean",
"default": false
},
"exclusiveMaximum": {
"type": "boolean",
"default": false
},
"minItems": {
"type": "integer",
"minimum": 0,
"default": 0
},
"maxItems": {
"type": "integer",
"minimum": 0
},
"uniqueItems": {
"type": "boolean",
"default": false
},
"pattern": {
"type": "string",
"format": "regex"
},
"minLength": {
"type": "integer",
"minimum": 0,
"default": 0
},
"maxLength": {
"type": "integer"
},
"enum": {
"type": "array",
"minItems": 1,
"uniqueItems": true
},
"default": {
"type": "any"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"format": {
"type": "string"
},
"divisibleBy": {
"type": "number",
"minimum": 0,
"exclusiveMinimum": true,
"default": 1
},
"disallow": {
"type": [ "string", "array" ],
"items": {
"type": [ "string", { "$ref": "#" } ]
},
"uniqueItems": true
},
"extends": {
"type": [ { "$ref": "#" }, "array" ],
"items": { "$ref": "#" },
"default": {}
},
"id": {
"type": "string",
"format": "uri"
},
"$ref": {
"type": "string",
"format": "uri"
},
"$schema": {
"type": "string",
"format": "uri"
}
},
"dependencies": {
"exclusiveMinimum": "minimum",
"exclusiveMaximum": "maximum"
},
"default": {}
}
2 changes: 2 additions & 0 deletions Src/Newtonsoft.Json.Schema/Infrastructure/DeferedSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
#endregion

using System;
using System.Diagnostics;

namespace Newtonsoft.Json.Schema.Infrastructure
{
[DebuggerDisplay("Reference = {ResolvedReference}, Success = {Success}")]
internal class DeferedSchema
{
public readonly Uri ResolvedReference;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ public static bool FindSchema(Action<JSchema> setSchema, JSchema schema, Uri roo
{
setSchema(s);
resolvedSchema = true;

// schema is a reference schema and needs to be resolved
if (s.Reference != null)
schemaReader.AddDeferedSchema(setSchema, s);
}
else
{
Expand Down
60 changes: 45 additions & 15 deletions Src/Newtonsoft.Json.Schema/Infrastructure/JSchemaReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ internal JSchema ReadInlineSchema(Action<JSchema> setSchema, JToken inlineToken)
return inlineToken.Annotation<JSchemaAnnotation>().Schema;
}

internal void AddDeferedSchema(Action<JSchema> setSchema, JSchema referenceSchema)
{
Uri resolvedReference = ResolveSchemaReference(referenceSchema);

DeferedSchema deferedSchema = new DeferedSchema(resolvedReference, referenceSchema, setSchema);
_deferedSchemas.Add(deferedSchema);
}

private JSchema ReadSchema(JsonReader reader, JSchema schema)
{
while (reader.Read())
Expand Down Expand Up @@ -91,6 +99,9 @@ public void ResolveDeferedSchemas()
{
List<DeferedSchema> resolvedDeferedSchemas = new List<DeferedSchema>();

int initialCount = _deferedSchemas.Count;

int i = 0;
// note that defered schemas could be added while resolving
while (_deferedSchemas.Count > 0)
{
Expand All @@ -100,6 +111,19 @@ public void ResolveDeferedSchemas()

resolvedDeferedSchemas.Add(deferedSchema);
_deferedSchemas.Remove(deferedSchema);

if (!deferedSchema.Success && i >= initialCount)
{
// if schema resolved to another reference and that reference has already not be resolved then fail
// probably a circular reference
for (int j = initialCount; j < resolvedDeferedSchemas.Count; j++)
{
DeferedSchema resolvedSchema = resolvedDeferedSchemas[j];
if (deferedSchema != resolvedSchema && deferedSchema.ResolvedReference.ToString() == resolvedSchema.ResolvedReference.ToString())
throw new JsonException("Could not resolve schema reference '{0}'.".FormatWith(CultureInfo.InvariantCulture, deferedSchema.ResolvedReference));
}
}
i++;
}

foreach (DeferedSchema resolvedDeferedSchema in resolvedDeferedSchemas)
Expand Down Expand Up @@ -527,21 +551,7 @@ private void LoadAndSetSchema(JsonReader reader, Action<JSchema> setSchema, bool

if (schema.Reference != null)
{
Uri resolvedReference = null;
foreach (JSchema s in _schemaStack.Reverse())
{
Uri part = s.Id;

if (part != null)
{
if (resolvedReference == null)
resolvedReference = part;
else
resolvedReference = SchemaDiscovery.ResolveSchemaId(resolvedReference, part);
}
}

resolvedReference = SchemaDiscovery.ResolveSchemaId(resolvedReference, schema.Reference);
Uri resolvedReference = ResolveSchemaReference(schema);

JSchema resolvedSchema = _resolver.GetSchema(resolvedReference);
if (resolvedSchema != null)
Expand All @@ -560,6 +570,26 @@ private void LoadAndSetSchema(JsonReader reader, Action<JSchema> setSchema, bool
_schemaStack.Pop();
}

private Uri ResolveSchemaReference(JSchema schema)
{
Uri resolvedReference = null;
foreach (JSchema s in _schemaStack.Reverse())
{
Uri part = s.Id;

if (part != null)
{
if (resolvedReference == null)
resolvedReference = part;
else
resolvedReference = SchemaDiscovery.ResolveSchemaId(resolvedReference, part);
}
}

resolvedReference = SchemaDiscovery.ResolveSchemaId(resolvedReference, schema.Reference);
return resolvedReference;
}

private void ProcessSchemaName(JsonReader reader, JSchema schema, string name)
{
switch (name)
Expand Down

0 comments on commit 6c43400

Please sign in to comment.