Skip to content

Commit

Permalink
Support deserialization of validation results with System.Text.Json (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremySkinner committed May 26, 2022
1 parent 587931e commit 4c4c583
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 7 deletions.
51 changes: 51 additions & 0 deletions src/FluentValidation.Tests/JsonSerializationTests.cs
@@ -0,0 +1,51 @@
namespace FluentValidation.Tests;

using System.Text.Json;
using Results;
using Xunit;

public class JsonSerializationTests {

[Fact]
public void SystemTextJson_deserialization_should_be_consistent_with_newtonsoft() {
var validationResult = new ValidationResult {
Errors = {
new ValidationFailure("MyProperty1", "Invalid MyProperty1"),
new ValidationFailure("MyProperty2", "Invalid MyProperty2"),
},
RuleSetsExecuted = new[] { "Test1" }
};

// System.Text.Json
var serialized1 = JsonSerializer.Serialize(validationResult);
var deserialized1 = JsonSerializer.Deserialize<ValidationResult>(serialized1);

// Newtonsoft.Json
var serialized2 = Newtonsoft.Json.JsonConvert.SerializeObject(validationResult);
var deserialized2 = Newtonsoft.Json.JsonConvert.DeserializeObject<ValidationResult>(serialized2);

deserialized1.IsValid.ShouldBeFalse();
deserialized2.IsValid.ShouldBeFalse();

deserialized1.Errors.Count.ShouldEqual(2);
deserialized2.Errors.Count.ShouldEqual(2);

deserialized1.Errors[0].PropertyName.ShouldEqual("MyProperty1");
deserialized2.Errors[0].PropertyName.ShouldEqual("MyProperty1");

deserialized1.Errors[1].PropertyName.ShouldEqual("MyProperty2");
deserialized2.Errors[1].PropertyName.ShouldEqual("MyProperty2");

deserialized1.Errors[0].ErrorMessage.ShouldEqual("Invalid MyProperty1");
deserialized2.Errors[0].ErrorMessage.ShouldEqual("Invalid MyProperty1");

deserialized1.Errors[1].ErrorMessage.ShouldEqual("Invalid MyProperty2");
deserialized2.Errors[1].ErrorMessage.ShouldEqual("Invalid MyProperty2");

deserialized1.RuleSetsExecuted.Length.ShouldEqual(1);
deserialized2.RuleSetsExecuted.Length.ShouldEqual(1);

deserialized1.RuleSetsExecuted[0].ShouldEqual("Test1");
deserialized2.RuleSetsExecuted[0].ShouldEqual("Test1");
}
}
7 changes: 6 additions & 1 deletion src/FluentValidation/Results/ValidationFailure.cs
Expand Up @@ -25,14 +25,19 @@ namespace FluentValidation.Results {
/// </summary>
[Serializable]
public class ValidationFailure {
private ValidationFailure() {

/// <summary>
/// Creates a new validation failure.
/// </summary>
public ValidationFailure() {

}

/// <summary>
/// Creates a new validation failure.
/// </summary>
public ValidationFailure(string propertyName, string errorMessage) : this(propertyName, errorMessage, null) {

}

/// <summary>
Expand Down
27 changes: 21 additions & 6 deletions src/FluentValidation/Results/ValidationResult.cs
Expand Up @@ -26,7 +26,7 @@ namespace FluentValidation.Results {
/// </summary>
[Serializable]
public class ValidationResult {
private readonly List<ValidationFailure> _errors;
private List<ValidationFailure> _errors;

/// <summary>
/// Whether validation succeeded
Expand All @@ -36,9 +36,23 @@ public class ValidationResult {
/// <summary>
/// A collection of errors
/// </summary>
public List<ValidationFailure> Errors => _errors;
public List<ValidationFailure> Errors {
get => _errors;
set {
if (value == null) {
throw new ArgumentNullException(nameof(value));
}

public string[] RuleSetsExecuted { get; internal set; }
// Ensure any nulls are removed and the list is copied
// to be consistent with the constructor below.
_errors = value.Where(failure => failure != null).ToList();;
}
}

/// <summary>
/// The RuleSets that were executed during the validation run.
/// </summary>
public string[] RuleSetsExecuted { get; set; }

/// <summary>
/// Creates a new validationResult
Expand All @@ -50,9 +64,10 @@ public class ValidationResult {
/// <summary>
/// Creates a new ValidationResult from a collection of failures
/// </summary>
/// <param name="failures">List of <see cref="ValidationFailure"/> which is later available through <see cref="Errors"/>. This list get's copied.</param>
/// <param name="failures">Collection of <see cref="ValidationFailure"/> instances which is later available through the <see cref="Errors"/> property.</param>
/// <remarks>
/// Every caller is responsible for not adding <c>null</c> to the list.
/// Any nulls will be excluded.
/// The list is copied.
/// </remarks>
public ValidationResult(IEnumerable<ValidationFailure> failures) {
_errors = failures.Where(failure => failure != null).ToList();
Expand All @@ -76,7 +91,7 @@ public class ValidationResult {
/// <param name="separator">The character to separate the error messages.</param>
/// <returns></returns>
public string ToString(string separator) {
return string.Join(separator, _errors.Select(failure => failure.ErrorMessage));
return string.Join(separator, _errors.Select(failure => failure.ErrorMessage));
}
}
}

0 comments on commit 4c4c583

Please sign in to comment.