Skip to content

Commit

Permalink
feat: Added support to serialize and deserialize custom erros
Browse files Browse the repository at this point in the history
  • Loading branch information
NelsonBN committed Mar 14, 2023
1 parent 652efe2 commit b69d0bd
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ var error = JsonSerializer.Deserialize<NotFoundError>(json);
```

For .NET 6.0 or greater, you can use the `IError` interface as the type to be deserialized.
To versions .NET 5.0 or lower, the deserialization using interface is not supported. You will get an exception `System.NotSupportedException`.
To versions .NET 5.0 or lower, the deserialization using interface is not supported by `System.Text.Json`. You will get an exception `System.NotSupportedException`.

```csharp
var error = JsonSerializer.Deserialize<IError>(json);
Expand Down
20 changes: 19 additions & 1 deletion src/CommonUtils.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;

namespace PowerUtils.Results
{
Expand Down Expand Up @@ -77,5 +79,21 @@ private static bool _hasError(ref List<IError> list)

return list.Count is not 0;
}

internal static Type GetErrorType(string fullName)
{
var type = Type.GetType(fullName, false, true);

type ??= AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.SingleOrDefault(s => s.FullName.Equals(fullName, StringComparison.InvariantCultureIgnoreCase));

if(type.GetInterfaces().Contains(typeof(IError)))
{
return type;
}

throw new TypeLoadException($"Could not load type '{fullName}'.");
}
}
}
4 changes: 2 additions & 2 deletions src/Serializers/ErrorJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace PowerUtils.Results.Serializers
{
internal sealed class ErrorJsonConverter<TError> : JsonConverter<TError>
public sealed class ErrorJsonConverter<TError> : JsonConverter<TError>
where TError : IError
{
private const string TYPE_NAME = "_type";
Expand Down Expand Up @@ -36,7 +36,7 @@ public override TError Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS

if(TYPE_NAME.Equals(propertyName, StringComparison.InvariantCultureIgnoreCase))
{
type = Type.GetType(reader.GetString());
type = CommonUtils.GetErrorType(reader.GetString());
}

else if(nameof(IError.Property).Equals(propertyName, StringComparison.InvariantCultureIgnoreCase))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using FluentAssertions;
using PowerUtils.Results.Tests.Fakes;
using Xunit;

namespace PowerUtils.Results.Tests.CommonUtilsTests
{
public class GetErrorTypeTests
{
[Fact]
public void BuiltInError_GetErrorType_Type()
{
// Arrange
var errorType = typeof(NotFoundError);
var fullName = errorType.FullName;


// Act
var act = CommonUtils.GetErrorType(fullName);


// Assert
act.Should().Be(errorType);
}

[Fact]
public void CustomError_GetErrorType_Type()
{
// Arrange
var errorType = typeof(CustomError);
var fullName = errorType.FullName;


// Act
var act = CommonUtils.GetErrorType(fullName);


// Assert
act.Should().Be(errorType);
}

[Fact]
public void ExistentTypeNotIError_GetErrorType_TypeLoadException()
{
// Arrange
var fullName = typeof(FakeCustomer).FullName;


// Act
var act = Record.Exception(() => CommonUtils.GetErrorType(fullName));


// Assert
act.Should().BeOfType<TypeLoadException>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
using System.Collections.Generic;
using System.Text.Json;
using FluentAssertions;
using FluentAssertions.Execution;
using PowerUtils.Results.Tests.Fakes;
using Xunit;

namespace PowerUtils.Results.Tests.Errors.CustomErrors
{
public class JsonSerializerDeserializeTests
{
[Fact]
public void EmptyArrayString_DeserializeToIErrorList_EmptyCustomErrorList()
{
// Arrange
var serialization = "[]";


// Act
var act = JsonSerializer.Deserialize<List<CustomError>>(serialization);


// Assert
using(new AssertionScope())
{
act.Should().BeEmpty();
act.Should().BeOfType<List<CustomError>>();
}
}

[Fact]
public void EmptyArrayString_DeserializeToIErrorArray_EmptyCustomErrorArray()
{
// Arrange
var serialization = "[]";


// Act
var act = JsonSerializer.Deserialize<CustomError[]>(serialization);


// Assert
using(new AssertionScope())
{
act.Should().BeEmpty();
act.Should().BeOfType<CustomError[]>();
}
}

[Fact]
public void String_Deserialize_CustomError()
{
// Arrange
var json = @"{
""_type"": ""PowerUtils.Results.Tests.Fakes.CustomError"",
""Property"": ""prop"",
""Code"": ""CODE"",
""Description"": ""Disc""
}";


// Act
var act = JsonSerializer.Deserialize<CustomError>(json);


// Assert
act.Should().IsError<CustomError>(
"prop",
"CODE",
"Disc");
}

[Fact]
public void CustomError_Serialize_SerializationWithTypePropertyCodeAndDescription()
{
// Arrange
var property = "prop";
var code = "cod";
var description = "disc";

var error = new CustomError(property, code, description);

var type = error.GetType().FullName;
var expected = $"{{\"_type\":\"{type}\",\"Property\":\"{property}\",\"Code\":\"{code}\",\"Description\":\"{description}\"}}";


// Act
var act = JsonSerializer.Serialize(error);


// Assert
act.Should().Be(expected);
}

[Fact]
public void CustomError_SerializeDeserialize_SameOriginalError()
{
// Arrange
var property = "prop";
var code = "cod";
var description = "disc";

var error = new CustomError(property, code, description);


// Act
var serialization = JsonSerializer.Serialize(error);
var act = JsonSerializer.Deserialize<CustomError>(serialization);


// Assert
act.Should().IsError<CustomError>(
property,
code,
description);
}

#if NET6_0_OR_GREATER
[Fact]
public void CustomError_DeserializeUsingIError_SameOriginalError()
{
// The System.Text.Json serializer does not support deserialization of interface types
// Exception returned -> 'System.NotSupportedException : Deserialization of interface types is not supported...'


// Arrange
var property = "prop";
var code = "cod";
var description = "disc";

var serialization = JsonSerializer.Serialize(
new CustomError(property, code, description));


// Act
var act = JsonSerializer.Deserialize<IError>(serialization);


// Assert
act.Should().IsError<CustomError>(
property,
code,
description);
}
#endif
}
}
6 changes: 5 additions & 1 deletion tests/PowerUtils.Results.Tests/Fakes/CustomError.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
namespace PowerUtils.Results.Tests.Fakes
using System.Text.Json.Serialization;
using PowerUtils.Results.Serializers;

namespace PowerUtils.Results.Tests.Fakes
{
[JsonConverter(typeof(ErrorJsonConverter<CustomError>))]
public class CustomError : IError
{
public string Property { get; init; }
Expand Down

0 comments on commit b69d0bd

Please sign in to comment.