-Added PatternProperties, ExclusiveMinimum and ExclusiveMaximum support to JSON schema

This commit is contained in:
JamesNK
2011-01-02 09:19:53 +00:00
parent 3ad33f7329
commit f00d796e03
12 changed files with 463 additions and 85 deletions
@@ -976,7 +976,7 @@ namespace Newtonsoft.Json.Tests
}
[Test]
public void sdfsdf()
public void NoAdditionalProperties()
{
string schemaJson = @"{
""type"":""array"",
@@ -1013,6 +1013,69 @@ namespace Newtonsoft.Json.Tests
Assert.IsFalse(reader.Read());
}
[Test]
public void PatternPropertiesNoAdditionalProperties()
{
string schemaJson = @"{
""type"":""object"",
""patternProperties"": {
""hi"": {""type"":""string""},
""ho"": {""type"":""string""}
},
""additionalProperties"": false
}";
string json = @"{
""hi"": ""A string!"",
""hide"": ""A string!"",
""ho"": 1,
""hey"": ""A string!""
}";
Json.Schema.ValidationEventArgs validationEventArgs = null;
JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json)));
reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; };
reader.Schema = JsonSchema.Parse(schemaJson);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.StartObject, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.PropertyName, reader.TokenType);
Assert.AreEqual(null, validationEventArgs);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.String, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.PropertyName, reader.TokenType);
Assert.AreEqual(null, validationEventArgs);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.String, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.PropertyName, reader.TokenType);
Assert.AreEqual(null, validationEventArgs);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.Integer, reader.TokenType);
Assert.AreEqual("Invalid type. Expected String but got Integer. Line 4, position 10.", validationEventArgs.Message);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.PropertyName, reader.TokenType);
Assert.AreEqual("Property 'hey' has not been defined and the schema does not allow additional properties. Line 5, position 8.", validationEventArgs.Message);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.String, reader.TokenType);
Assert.IsTrue(reader.Read());
Assert.AreEqual(JsonToken.EndObject, reader.TokenType);
Assert.IsFalse(reader.Read());
}
[Test]
public void ExtendedComplex()
{
@@ -217,5 +217,64 @@ namespace Newtonsoft.Json.Tests.Schema
Assert.AreEqual(1, errors.Count);
Assert.AreEqual("Property 'g' has not been defined and the schema does not allow additional properties. Line 1, position 5.", errors[0]);
}
[Test]
[ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 equals maximum value of 10 and exclusive maximum is true.")]
public void ExclusiveMaximum_Int()
{
JsonSchema schema = new JsonSchema();
schema.Maximum = 10;
schema.ExclusiveMaximum = true;
JValue v = new JValue(10);
v.Validate(schema);
}
[Test]
[ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Float 10.1 equals maximum value of 10.1 and exclusive maximum is true.")]
public void ExclusiveMaximum_Float()
{
JsonSchema schema = new JsonSchema();
schema.Maximum = 10.1;
schema.ExclusiveMaximum = true;
JValue v = new JValue(10.1);
v.Validate(schema);
}
[Test]
[ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 equals minimum value of 10 and exclusive minimum is true.")]
public void ExclusiveMinimum_Int()
{
JsonSchema schema = new JsonSchema();
schema.Minimum = 10;
schema.ExclusiveMinimum = true;
JValue v = new JValue(10);
v.Validate(schema);
}
[Test]
[ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Float 10.1 equals minimum value of 10.1 and exclusive minimum is true.")]
public void ExclusiveMinimum_Float()
{
JsonSchema schema = new JsonSchema();
schema.Minimum = 10.1;
schema.ExclusiveMinimum = true;
JValue v = new JValue(10.1);
v.Validate(schema);
}
[Test]
[ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 is not evenly divisible by 3.")]
public void DivisibleBy_Int()
{
JsonSchema schema = new JsonSchema();
schema.DivisibleBy = 3;
JValue v = new JValue(10);
v.Validate(schema);
}
}
}
@@ -130,6 +130,21 @@ namespace Newtonsoft.Json.Tests.Schema
Assert.AreEqual(true, schema.Required);
}
[Test]
public void ExclusiveMinimum_ExclusiveMaximum()
{
string json = @"{
""exclusiveMinimum"":true,
""exclusiveMaximum"":true
}";
JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver());
JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json)));
Assert.AreEqual(true, schema.ExclusiveMinimum);
Assert.AreEqual(true, schema.ExclusiveMaximum);
}
[Test]
public void ReadOnly()
{
@@ -433,5 +448,22 @@ namespace Newtonsoft.Json.Tests.Schema
JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver());
JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json)));
}
[Test]
public void PatternProperties()
{
string json = @"{
""patternProperties"": {
""[abc]"": { ""id"":""Blah"" }
}
}";
JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver());
JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json)));
Assert.IsNotNull(schema.PatternProperties);
Assert.AreEqual(1, schema.PatternProperties.Count);
Assert.AreEqual("Blah", schema.PatternProperties["[abc]"].Id);
}
}
}
@@ -377,5 +377,50 @@ namespace Newtonsoft.Json.Tests.Schema
Assert.AreEqual(schema.Properties["x"].Options[1], "Asc");
Assert.AreEqual(schema.Properties["x"].Options[-1], "Desc");
}
[Test]
public void WriteTo_ExclusiveMinimum_ExclusiveMaximum()
{
JsonSchema schema = new JsonSchema();
schema.ExclusiveMinimum = true;
schema.ExclusiveMaximum = true;
StringWriter writer = new StringWriter();
JsonTextWriter jsonWriter = new JsonTextWriter(writer);
jsonWriter.Formatting = Formatting.Indented;
schema.WriteTo(jsonWriter);
string json = writer.ToString();
Assert.AreEqual(@"{
""exclusiveMinimum"": true,
""exclusiveMaximum"": true
}", json);
}
[Test]
public void WriteTo_PatternProperties()
{
JsonSchema schema = new JsonSchema();
schema.PatternProperties = new Dictionary<string, JsonSchema>
{
{ "[abc]", new JsonSchema() }
};
StringWriter writer = new StringWriter();
JsonTextWriter jsonWriter = new JsonTextWriter(writer);
jsonWriter.Formatting = Formatting.Indented;
schema.WriteTo(jsonWriter);
string json = writer.ToString();
Assert.AreEqual(@"{
""patternProperties"": {
""[abc]"": {}
}
}", json);
}
}
}
+161 -61
View File
@@ -43,15 +43,15 @@ namespace Newtonsoft.Json
private class SchemaScope
{
private readonly JTokenType _tokenType;
private readonly JsonSchemaModel _schema;
private readonly IList<JsonSchemaModel> _schemas;
private readonly Dictionary<string, bool> _requiredProperties;
public string CurrentPropertyName { get; set; }
public int ArrayItemCount { get; set; }
public JsonSchemaModel Schema
public IList<JsonSchemaModel> Schemas
{
get { return _schema; }
get { return _schemas; }
}
public Dictionary<string, bool> RequiredProperties
@@ -64,19 +64,19 @@ namespace Newtonsoft.Json
get { return _tokenType; }
}
public SchemaScope(JTokenType tokenType, JsonSchemaModel schema)
public SchemaScope(JTokenType tokenType, IList<JsonSchemaModel> schemas)
{
_tokenType = tokenType;
_schema = schema;
_schemas = schemas;
if (_schema != null && _schema.Properties != null)
_requiredProperties = GetRequiredProperties(_schema).Distinct().ToDictionary(p => p, p => false);
else
_requiredProperties = new Dictionary<string, bool>();
_requiredProperties = schemas.SelectMany(GetRequiredProperties).Distinct().ToDictionary(p => p, p => false);
}
private IEnumerable<string> GetRequiredProperties(JsonSchemaModel schema)
{
if (schema == null || schema.Properties == null)
return Enumerable.Empty<string>();
return schema.Properties.Where(p => p.Value.Required).Select(p => p.Key);
}
}
@@ -154,47 +154,79 @@ namespace Newtonsoft.Json
return poppedScope;
}
private JsonSchemaModel CurrentSchema
private IEnumerable<JsonSchemaModel> CurrentSchemas
{
get { return _currentScope.Schema; }
get { return _currentScope.Schemas; }
}
private JsonSchemaModel CurrentMemberSchema
private IEnumerable<JsonSchemaModel> CurrentMemberSchemas
{
get
{
if (_currentScope == null)
return _model;
return new List<JsonSchemaModel>(new [] { _model });
if (_currentScope.Schema == null)
return null;
if (_currentScope.Schemas == null || _currentScope.Schemas.Count == 0)
return Enumerable.Empty<JsonSchemaModel>();
switch (_currentScope.TokenType)
{
case JTokenType.None:
return _currentScope.Schema;
return _currentScope.Schemas;
case JTokenType.Object:
if (_currentScope.CurrentPropertyName == null)
throw new Exception("CurrentPropertyName has not been set on scope.");
JsonSchemaModel propertySchema;
if (CurrentSchema.Properties != null && CurrentSchema.Properties.TryGetValue(_currentScope.CurrentPropertyName, out propertySchema))
return propertySchema;
return (CurrentSchema.AllowAdditionalProperties) ? CurrentSchema.AdditionalProperties : null;
case JTokenType.Array:
if (!CollectionUtils.IsNullOrEmpty(CurrentSchema.Items))
{
if (CurrentSchema.Items.Count == 1)
return CurrentSchema.Items[0];
if (_currentScope.CurrentPropertyName == null)
throw new Exception("CurrentPropertyName has not been set on scope.");
if (CurrentSchema.Items.Count > (_currentScope.ArrayItemCount - 1))
return CurrentSchema.Items[_currentScope.ArrayItemCount - 1];
IList<JsonSchemaModel> schemas = new List<JsonSchemaModel>();
foreach (JsonSchemaModel schema in CurrentSchemas)
{
JsonSchemaModel propertySchema;
if (schema.Properties != null && schema.Properties.TryGetValue(_currentScope.CurrentPropertyName, out propertySchema))
{
schemas.Add(propertySchema);
}
if (schema.PatternProperties != null)
{
foreach (KeyValuePair<string, JsonSchemaModel> patternProperty in schema.PatternProperties)
{
if (Regex.IsMatch(_currentScope.CurrentPropertyName, patternProperty.Key))
{
schemas.Add(patternProperty.Value);
}
}
}
if (schemas.Count == 0 && schema.AllowAdditionalProperties && schema.AdditionalProperties != null)
schemas.Add(schema.AdditionalProperties);
}
return schemas;
}
case JTokenType.Array:
{
IList<JsonSchemaModel> schemas = new List<JsonSchemaModel>();
foreach (JsonSchemaModel schema in CurrentSchemas)
{
if (!CollectionUtils.IsNullOrEmpty(schema.Items))
{
if (schema.Items.Count == 1)
schemas.Add(schema.Items[0]);
return (CurrentSchema.AllowAdditionalProperties) ? CurrentSchema.AdditionalProperties : null;
if (schema.Items.Count > (_currentScope.ArrayItemCount - 1))
schemas.Add(schema.Items[_currentScope.ArrayItemCount - 1]);
}
if (schema.AllowAdditionalProperties && schema.AdditionalProperties != null)
schemas.Add(schema.AdditionalProperties);
}
return schemas;
}
case JTokenType.Constructor:
return null;
return Enumerable.Empty<JsonSchemaModel>();
default:
throw new ArgumentOutOfRangeException("TokenType", "Unexpected token type: {0}".FormatWith(CultureInfo.InvariantCulture, _currentScope.TokenType));
}
@@ -371,58 +403,80 @@ namespace Newtonsoft.Json
_model = builder.Build(_schema);
}
//ValidateValueToken();
switch (_reader.TokenType)
{
case JsonToken.StartObject:
ProcessValue();
JsonSchemaModel objectSchema = (ValidateObject(CurrentMemberSchema))
? CurrentMemberSchema
: null;
Push(new SchemaScope(JTokenType.Object, objectSchema));
IList<JsonSchemaModel> objectSchemas = CurrentMemberSchemas.Where(ValidateObject).ToList();
Push(new SchemaScope(JTokenType.Object, objectSchemas));
break;
case JsonToken.StartArray:
ProcessValue();
JsonSchemaModel arraySchema = (ValidateArray(CurrentMemberSchema))
? CurrentMemberSchema
: null;
Push(new SchemaScope(JTokenType.Array, arraySchema));
IList<JsonSchemaModel> arraySchemas = CurrentMemberSchemas.Where(ValidateArray).ToList();
Push(new SchemaScope(JTokenType.Array, arraySchemas));
break;
case JsonToken.StartConstructor:
Push(new SchemaScope(JTokenType.Constructor, null));
break;
case JsonToken.PropertyName:
ValidatePropertyName(CurrentSchema);
foreach (JsonSchemaModel schema in CurrentSchemas)
{
ValidatePropertyName(schema);
}
break;
case JsonToken.Raw:
break;
case JsonToken.Integer:
ProcessValue();
ValidateInteger(CurrentMemberSchema);
foreach (JsonSchemaModel schema in CurrentMemberSchemas)
{
ValidateInteger(schema);
}
break;
case JsonToken.Float:
ProcessValue();
ValidateFloat(CurrentMemberSchema);
foreach (JsonSchemaModel schema in CurrentMemberSchemas)
{
ValidateFloat(schema);
}
break;
case JsonToken.String:
ProcessValue();
ValidateString(CurrentMemberSchema);
foreach (JsonSchemaModel schema in CurrentMemberSchemas)
{
ValidateString(schema);
}
break;
case JsonToken.Boolean:
ProcessValue();
ValidateBoolean(CurrentMemberSchema);
foreach (JsonSchemaModel schema in CurrentMemberSchemas)
{
ValidateBoolean(schema);
}
break;
case JsonToken.Null:
ProcessValue();
ValidateNull(CurrentMemberSchema);
foreach (JsonSchemaModel schema in CurrentMemberSchemas)
{
ValidateNull(schema);
}
break;
case JsonToken.Undefined:
break;
case JsonToken.EndObject:
ValidateEndObject(CurrentSchema);
foreach (JsonSchemaModel schema in CurrentSchemas)
{
ValidateEndObject(schema);
}
Pop();
break;
case JsonToken.EndArray:
ValidateEndArray(CurrentSchema);
foreach (JsonSchemaModel schema in CurrentSchemas)
{
ValidateEndArray(schema);
}
Pop();
break;
case JsonToken.EndConstructor:
@@ -528,11 +582,24 @@ namespace Newtonsoft.Json
long value = Convert.ToInt64(_reader.Value, CultureInfo.InvariantCulture);
if (schema.Maximum != null && value > schema.Maximum)
RaiseError("Integer {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema);
if (schema.Maximum != null)
{
if (value > schema.Maximum)
RaiseError("Integer {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema);
if (schema.ExclusiveMaximum && value == schema.Maximum)
RaiseError("Integer {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema);
}
if (schema.Minimum != null && value < schema.Minimum)
RaiseError("Integer {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema);
if (schema.Minimum != null)
{
if (value < schema.Minimum)
RaiseError("Integer {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema);
if (schema.ExclusiveMinimum && value == schema.Minimum)
RaiseError("Integer {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema);
}
if (schema.DivisibleBy != null && !IsZero(value % schema.DivisibleBy.Value))
RaiseError("Integer {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema);
}
private void ProcessValue()
@@ -541,8 +608,11 @@ namespace Newtonsoft.Json
{
_currentScope.ArrayItemCount++;
if (CurrentSchema != null && CurrentSchema.Items != null && CurrentSchema.Items.Count > 1 && _currentScope.ArrayItemCount >= CurrentSchema.Items.Count)
RaiseError("Index {0} has not been defined and the schema does not allow additional items.".FormatWith(CultureInfo.InvariantCulture, _currentScope.ArrayItemCount), CurrentSchema);
foreach (JsonSchemaModel currentSchema in CurrentSchemas)
{
if (currentSchema != null && currentSchema.Items != null && currentSchema.Items.Count > 1 && _currentScope.ArrayItemCount >= currentSchema.Items.Count)
RaiseError("Index {0} has not been defined and the schema does not allow additional items.".FormatWith(CultureInfo.InvariantCulture, _currentScope.ArrayItemCount), currentSchema);
}
}
}
@@ -558,11 +628,21 @@ namespace Newtonsoft.Json
double value = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture);
if (schema.Maximum != null && value > schema.Maximum)
RaiseError("Float {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema);
if (schema.Maximum != null)
{
if (value > schema.Maximum)
RaiseError("Float {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema);
if (schema.ExclusiveMaximum && value == schema.Maximum)
RaiseError("Float {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema);
}
if (schema.Minimum != null && value < schema.Minimum)
RaiseError("Float {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema);
if (schema.Minimum != null)
{
if (value < schema.Minimum)
RaiseError("Float {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema);
if (schema.ExclusiveMinimum && value == schema.Minimum)
RaiseError("Float {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema);
}
if (schema.DivisibleBy != null && !IsZero(value % schema.DivisibleBy.Value))
RaiseError("Float {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema);
@@ -585,14 +665,34 @@ namespace Newtonsoft.Json
if (_currentScope.RequiredProperties.ContainsKey(propertyName))
_currentScope.RequiredProperties[propertyName] = true;
bool propertyDefinied = (schema.Properties != null && schema.Properties.ContainsKey(propertyName));
if (!schema.AllowAdditionalProperties)
{
bool propertyDefinied = IsPropertyDefinied(schema, propertyName);
if (!propertyDefinied && !schema.AllowAdditionalProperties)
RaiseError("Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema);
if (!propertyDefinied)
RaiseError("Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema);
}
_currentScope.CurrentPropertyName = propertyName;
}
private bool IsPropertyDefinied(JsonSchemaModel schema, string propertyName)
{
if (schema.Properties != null && schema.Properties.ContainsKey(propertyName))
return true;
if (schema.PatternProperties != null)
{
foreach (string pattern in schema.PatternProperties.Keys)
{
if (Regex.IsMatch(propertyName, pattern))
return true;
}
}
return false;
}
private bool ValidateArray(JsonSchemaModel schema)
{
if (schema == null)
+17 -2
View File
@@ -89,9 +89,9 @@ namespace Newtonsoft.Json.Schema
/// <value>The maximum length.</value>
public int? MaximumLength { get; set; }
/// <summary>
/// Gets or sets the maximum decimals.
/// Gets or sets a number that the value should be divisble by.
/// </summary>
/// <value>The maximum decimals.</value>
/// <value>A number that the value should be divisble by.</value>
public double? DivisibleBy { get; set; }
/// <summary>
/// Gets or sets the minimum.
@@ -104,6 +104,16 @@ namespace Newtonsoft.Json.Schema
/// <value>The maximum.</value>
public double? Maximum { get; set; }
/// <summary>
/// Gets or sets a flag indicating whether the value can not equal the number defined by the "minimum" attribute.
/// </summary>
/// <value>A flag indicating whether the value can not equal the number defined by the "minimum" attribute.</value>
public bool? ExclusiveMinimum { get; set; }
/// <summary>
/// Gets or sets a flag indicating whether the value can not equal the number defined by the "maximum" attribute.
/// </summary>
/// <value>A flag indicating whether the value can not equal the number defined by the "maximum" attribute.</value>
public bool? ExclusiveMaximum { get; set; }
/// <summary>
/// Gets or sets the minimum number of items.
/// </summary>
/// <value>The minimum number of items.</value>
@@ -129,6 +139,11 @@ namespace Newtonsoft.Json.Schema
/// <value>The <see cref="JsonSchema"/> of additional properties.</value>
public JsonSchema AdditionalProperties { get; set; }
/// <summary>
/// Gets or sets the pattern properties.
/// </summary>
/// <value>The pattern properties.</value>
public IDictionary<string, JsonSchema> PatternProperties { get; set; }
/// <summary>
/// Gets or sets a value indicating whether additional properties are allowed.
/// </summary>
/// <value>
@@ -146,6 +146,9 @@ namespace Newtonsoft.Json.Schema
case JsonSchemaConstants.AdditionalPropertiesPropertyName:
ProcessAdditionalProperties();
break;
case JsonSchemaConstants.PatternPropertiesPropertyName:
ProcessPatternProperties();
break;
case JsonSchemaConstants.RequiredPropertyName:
CurrentSchema.Required = (bool)_reader.Value;
break;
@@ -161,6 +164,12 @@ namespace Newtonsoft.Json.Schema
case JsonSchemaConstants.MaximumPropertyName:
CurrentSchema.Maximum = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture);
break;
case JsonSchemaConstants.ExclusiveMinimumPropertyName:
CurrentSchema.ExclusiveMinimum = (bool)_reader.Value;
break;
case JsonSchemaConstants.ExclusiveMaximumPropertyName:
CurrentSchema.ExclusiveMaximum = (bool)_reader.Value;
break;
case JsonSchemaConstants.MaximumLengthPropertyName:
CurrentSchema.MaximumLength = Convert.ToInt32(_reader.Value, CultureInfo.InvariantCulture);
break;
@@ -311,6 +320,27 @@ namespace Newtonsoft.Json.Schema
CurrentSchema.AdditionalProperties = BuildSchema();
}
private void ProcessPatternProperties()
{
Dictionary<string, JsonSchema> patternProperties = new Dictionary<string, JsonSchema>();
if (_reader.TokenType != JsonToken.StartObject)
throw new Exception("Expected start object token.");
while (_reader.Read() && _reader.TokenType != JsonToken.EndObject)
{
string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture);
_reader.Read();
if (patternProperties.ContainsKey(propertyName))
throw new Exception("Property {0} has already been defined in schema.".FormatWith(CultureInfo.InvariantCulture, propertyName));
patternProperties.Add(propertyName, BuildSchema());
}
CurrentSchema.PatternProperties = patternProperties;
}
private void ProcessItems()
{
CurrentSchema.Items = new List<JsonSchema>();
@@ -36,11 +36,14 @@ namespace Newtonsoft.Json.Schema
public const string PropertiesPropertyName = "properties";
public const string ItemsPropertyName = "items";
public const string RequiredPropertyName = "required";
public const string PatternPropertiesPropertyName = "patternProperties";
public const string AdditionalPropertiesPropertyName = "additionalProperties";
public const string RequiresPropertyName = "requires";
public const string IdentityPropertyName = "identity";
public const string MinimumPropertyName = "minimum";
public const string MaximumPropertyName = "maximum";
public const string ExclusiveMinimumPropertyName = "exclusiveMinimum";
public const string ExclusiveMaximumPropertyName = "exclusiveMaximum";
public const string MinimumItemsPropertyName = "minItems";
public const string MaximumItemsPropertyName = "maxItems";
public const string PatternPropertyName = "pattern";
@@ -40,11 +40,14 @@ namespace Newtonsoft.Json.Schema
public double? DivisibleBy { get; set; }
public double? Minimum { get; set; }
public double? Maximum { get; set; }
public bool ExclusiveMinimum { get; set; }
public bool ExclusiveMaximum { get; set; }
public int? MinimumItems { get; set; }
public int? MaximumItems { get; set; }
public IList<string> Patterns { get; set; }
public IList<JsonSchemaModel> Items { get; set; }
public IDictionary<string, JsonSchemaModel> Properties { get; set; }
public IDictionary<string, JsonSchemaModel> PatternProperties { get; set; }
public JsonSchemaModel AdditionalProperties { get; set; }
public bool AllowAdditionalProperties { get; set; }
public IList<JToken> Enum { get; set; }
@@ -83,6 +86,9 @@ namespace Newtonsoft.Json.Schema
model.Minimum = MathUtils.Max(model.Minimum, schema.Minimum);
model.Maximum = MathUtils.Max(model.Maximum, schema.Maximum);
model.ExclusiveMinimum = model.ExclusiveMinimum || (schema.ExclusiveMinimum ?? false);
model.ExclusiveMaximum = model.ExclusiveMaximum || (schema.ExclusiveMaximum ?? false);
model.MinimumItems = MathUtils.Max(model.MinimumItems, schema.MinimumItems);
model.MaximumItems = MathUtils.Min(model.MaximumItems, schema.MaximumItems);
model.AllowAdditionalProperties = model.AllowAdditionalProperties && schema.AllowAdditionalProperties;
@@ -71,13 +71,9 @@ namespace Newtonsoft.Json.Schema
_nodes.Add(currentNode);
if (schema.Properties != null)
{
foreach (KeyValuePair<string, JsonSchema> property in schema.Properties)
{
AddProperty(currentNode, property.Key, property.Value);
}
}
AddProperties(schema.Properties, currentNode.Properties);
AddProperties(schema.PatternProperties, currentNode.PatternProperties);
if (schema.Items != null)
{
@@ -96,12 +92,23 @@ namespace Newtonsoft.Json.Schema
return currentNode;
}
public void AddProperty(JsonSchemaNode parentNode, string propertyName, JsonSchema schema)
public void AddProperties(IDictionary<string, JsonSchema> source, IDictionary<string, JsonSchemaNode> target)
{
if (source != null)
{
foreach (KeyValuePair<string, JsonSchema> property in source)
{
AddProperty(target, property.Key, property.Value);
}
}
}
public void AddProperty(IDictionary<string, JsonSchemaNode> target, string propertyName, JsonSchema schema)
{
JsonSchemaNode propertyNode;
parentNode.Properties.TryGetValue(propertyName, out propertyNode);
target.TryGetValue(propertyName, out propertyNode);
parentNode.Properties[propertyName] = AddSchema(propertyNode, schema);
target[propertyName] = AddSchema(propertyNode, schema);
}
public void AddItem(JsonSchemaNode parentNode, int index, JsonSchema schema)
@@ -143,6 +150,13 @@ namespace Newtonsoft.Json.Schema
model.Properties[property.Key] = BuildNodeModel(property.Value);
}
foreach (KeyValuePair<string, JsonSchemaNode> property in node.PatternProperties)
{
if (model.PatternProperties == null)
model.PatternProperties = new Dictionary<string, JsonSchemaModel>();
model.PatternProperties[property.Key] = BuildNodeModel(property.Value);
}
for (int i = 0; i < node.Items.Count; i++)
{
if (model.Items == null)
@@ -35,6 +35,7 @@ namespace Newtonsoft.Json.Schema
public string Id { get; private set; }
public ReadOnlyCollection<JsonSchema> Schemas { get; private set; }
public Dictionary<string, JsonSchemaNode> Properties { get; private set; }
public Dictionary<string, JsonSchemaNode> PatternProperties { get; private set; }
public List<JsonSchemaNode> Items { get; private set; }
public JsonSchemaNode AdditionalProperties { get; set; }
@@ -42,6 +43,7 @@ namespace Newtonsoft.Json.Schema
{
Schemas = new ReadOnlyCollection<JsonSchema>(new []{ schema });
Properties = new Dictionary<string, JsonSchemaNode>();
PatternProperties = new Dictionary<string, JsonSchemaNode>();
Items = new List<JsonSchemaNode>();
Id = GetId(Schemas);
@@ -51,6 +53,7 @@ namespace Newtonsoft.Json.Schema
{
Schemas = new ReadOnlyCollection<JsonSchema>(source.Schemas.Union(new[] { schema }).ToList());
Properties = new Dictionary<string, JsonSchemaNode>(source.Properties);
PatternProperties = new Dictionary<string, JsonSchemaNode>(source.PatternProperties);
Items = new List<JsonSchemaNode>(source.Items);
AdditionalProperties = source.AdditionalProperties;
+19 -11
View File
@@ -89,20 +89,13 @@ namespace Newtonsoft.Json.Schema
ReferenceOrWriteSchema(schema.AdditionalProperties);
}
}
if (schema.Properties != null)
{
_writer.WritePropertyName(JsonSchemaConstants.PropertiesPropertyName);
_writer.WriteStartObject();
foreach (KeyValuePair<string, JsonSchema> property in schema.Properties)
{
_writer.WritePropertyName(property.Key);
ReferenceOrWriteSchema(property.Value);
}
_writer.WriteEndObject();
}
WriteSchemaDictionaryIfNotNull(_writer, JsonSchemaConstants.PropertiesPropertyName, schema.Properties);
WriteSchemaDictionaryIfNotNull(_writer, JsonSchemaConstants.PatternPropertiesPropertyName, schema.PatternProperties);
WriteItems(schema);
WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumPropertyName, schema.Minimum);
WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumPropertyName, schema.Maximum);
WritePropertyIfNotNull(_writer, JsonSchemaConstants.ExclusiveMinimumPropertyName, schema.ExclusiveMinimum);
WritePropertyIfNotNull(_writer, JsonSchemaConstants.ExclusiveMaximumPropertyName, schema.ExclusiveMaximum);
WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumLengthPropertyName, schema.MinimumLength);
WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumLengthPropertyName, schema.MaximumLength);
WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumItemsPropertyName, schema.MinimumItems);
@@ -153,6 +146,21 @@ namespace Newtonsoft.Json.Schema
_writer.WriteEndObject();
}
private void WriteSchemaDictionaryIfNotNull(JsonWriter writer, string propertyName, IDictionary<string, JsonSchema> properties)
{
if (properties != null)
{
writer.WritePropertyName(propertyName);
writer.WriteStartObject();
foreach (KeyValuePair<string, JsonSchema> property in properties)
{
writer.WritePropertyName(property.Key);
ReferenceOrWriteSchema(property.Value);
}
writer.WriteEndObject();
}
}
private void WriteItems(JsonSchema schema)
{
if (CollectionUtils.IsNullOrEmpty(schema.Items))