-Added PatternProperties, ExclusiveMinimum and ExclusiveMaximum support to JSON schema
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user