Navigation Menu

Skip to content

Commit

Permalink
Improve multi type validation, closes #695
Browse files Browse the repository at this point in the history
  • Loading branch information
RicoSuter committed May 8, 2018
1 parent 737d201 commit 4627018
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 11 deletions.
52 changes: 52 additions & 0 deletions src/NJsonSchema.Tests/Validation/SchemaTests.cs
Expand Up @@ -340,5 +340,57 @@ public async Task When_no_additional_properties_are_allowed_then_this_error_is_r
Assert.Equal("#/Key", error.Path);
Assert.Same(schema, error.Schema);
}

[Fact]
public async Task When_multiple_types_fail_with_errors_take_the_best_group()
{
//// Arrange
var schemaJson = @"{
""$schema"": ""http://json-schema.org/schema#"",
""type"": ""object"",
""properties"": {
""name"": {
""type"": ""string"",
""maxLength"": 40
},
""settings"": {
""type"": [ ""object"", ""null"" ],
""properties"": {
""security"": {
""type"": [ ""object"", ""null"" ],
""properties"": {
""timeout"": {
""type"": [ ""integer"", ""null"" ],
""minimum"": 1,
""maximum"": 10
}
},
""additionalProperties"": false
}
},
""additionalProperties"": false
}
},
""required"": [ ""name"" ],
""additionalProperties"": false
}";

var json = @"{
""name"":""abc"",
""settings"": {
""security"":{
""timeout"": 0
}
}
}";

//// Act
var schema = await JsonSchema4.FromJsonAsync(schemaJson);
var errors = schema.Validate(json);

//// Assert
Assert.Equal(1, errors.Count);
Assert.Contains(errors, e => e.Kind == ValidationErrorKind.NoTypeValidates);
}
}
}
23 changes: 13 additions & 10 deletions src/NJsonSchema/Validation/JsonSchemaValidator.cs
Expand Up @@ -60,23 +60,26 @@ protected virtual ICollection<ValidationError> Validate(JToken token, JsonSchema

private void ValidateType(JToken token, JsonSchema4 schema, string propertyName, string propertyPath, List<ValidationError> errors)
{
var types = GetTypes(schema).ToDictionary(t => t, t => new List<ValidationError>());
if (types.Count > 0)
var types = GetTypes(schema).ToDictionary(t => t, t => (ICollection<ValidationError>)new List<ValidationError>());
if (types.Count > 1)
{
foreach (var type in types)
{
ValidateArray(token, schema, type.Key, propertyName, propertyPath, type.Value);
ValidateString(token, schema, type.Key, propertyName, propertyPath, type.Value);
ValidateNumber(token, schema, type.Key, propertyName, propertyPath, type.Value);
ValidateInteger(token, schema, type.Key, propertyName, propertyPath, type.Value);
ValidateBoolean(token, schema, type.Key, propertyName, propertyPath, type.Value);
ValidateNull(token, schema, type.Key, propertyName, propertyPath, type.Value);
ValidateObject(token, schema, type.Key, propertyName, propertyPath, type.Value);
ValidateArray(token, schema, type.Key, propertyName, propertyPath, (List<ValidationError>)type.Value);
ValidateString(token, schema, type.Key, propertyName, propertyPath, (List<ValidationError>)type.Value);
ValidateNumber(token, schema, type.Key, propertyName, propertyPath, (List<ValidationError>)type.Value);
ValidateInteger(token, schema, type.Key, propertyName, propertyPath, (List<ValidationError>)type.Value);
ValidateBoolean(token, schema, type.Key, propertyName, propertyPath, (List<ValidationError>)type.Value);
ValidateNull(token, schema, type.Key, propertyName, propertyPath, (List<ValidationError>)type.Value);
ValidateObject(token, schema, type.Key, propertyName, propertyPath, (List<ValidationError>)type.Value);
}

// just one has to validate when multiple types are defined
if (types.All(t => t.Value.Count > 0))
errors.AddRange(types.SelectMany(t => t.Value));
{
errors.Add(new MultiTypeValidationError(
ValidationErrorKind.NoTypeValidates, propertyName, propertyPath, types, token, schema));
}
}
else
{
Expand Down
59 changes: 59 additions & 0 deletions src/NJsonSchema/Validation/MultiTypeValidationError.cs
@@ -0,0 +1,59 @@
//-----------------------------------------------------------------------
// <copyright file="MultiTypeValidationError.cs" company="NJsonSchema">
// Copyright (c) Rico Suter. All rights reserved.
// </copyright>
// <license>https://github.com/rsuter/NJsonSchema/blob/master/LICENSE.md</license>
// <author>Rico Suter, mail@rsuter.com</author>
//-----------------------------------------------------------------------

using System.Collections.Generic;
using Newtonsoft.Json.Linq;

namespace NJsonSchema.Validation
{
/// <summary>A multi type validation error.</summary>
public class MultiTypeValidationError : ValidationError
{
/// <summary>Initializes a new instance of the <see cref="ValidationError"/> class. </summary>
/// <param name="kind">The error kind. </param>
/// <param name="property">The property name. </param>
/// <param name="path">The property path. </param>
/// <param name="errors">The error list. </param>
/// <param name="token">The token that failed to validate. </param>
/// <param name="schema">The schema that contains the validation rule.</param>
#if !LEGACY
public MultiTypeValidationError(ValidationErrorKind kind, string property, string path, IReadOnlyDictionary<JsonObjectType, ICollection<ValidationError>> errors, JToken token, JsonSchema4 schema)
#else
public MultiTypeValidationError(ValidationErrorKind kind, string property, string path, IDictionary<JsonObjectType, ICollection<ValidationError>> errors, JToken token, JsonSchema4 schema)
#endif
: base(kind, property, path, token, schema)
{
Errors = errors;
}

/// <summary>Gets the errors for each validated type. </summary>
#if !LEGACY
public IReadOnlyDictionary<JsonObjectType, ICollection<ValidationError>> Errors { get; private set; }
#else
public IDictionary<JsonObjectType, ICollection<ValidationError>> Errors { get; private set; }
#endif

/// <summary>Returns a string that represents the current object.</summary>
/// <returns>A string that represents the current object.</returns>
/// <filterpriority>2</filterpriority>
public override string ToString()
{
var output = string.Format("{0}: {1}\n", Kind, Path);
foreach (var error in Errors)
{
output += "{" + error.Key + ":\n";
foreach (var validationError in error.Value)
{
output += string.Format(" {0}\n", validationError.ToString().Replace("\n", "\n "));
}
output += "}\n";
}
return output;
}
}
}
5 changes: 4 additions & 1 deletion src/NJsonSchema/Validation/ValidationErrorKind.cs
Expand Up @@ -138,6 +138,9 @@ public enum ValidationErrorKind
TooFewProperties,

/// <summary>A Base64 string is expected. </summary>
Base64Expected
Base64Expected,

/// <summary>No type of the types does validate (check error details in <see cref="MultiTypeValidationError"/>). </summary>
NoTypeValidates
}
}

0 comments on commit 4627018

Please sign in to comment.