-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
14761c8
commit 62fd7d3
Showing
13 changed files
with
405 additions
and
4 deletions.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
DotNetThoughts.Results.Json.Tests/DotNetThoughts.Results.Json.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="FluentAssertions" Version="6.12.0" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" /> | ||
<PackageReference Include="Verify.Xunit" Version="25.0.1" /> | ||
<PackageReference Include="xunit.core" Version="2.8.1" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\DotNetThoughts.Results.Json\DotNetThoughts.Results.Json.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
14 changes: 14 additions & 0 deletions
14
DotNetThoughts.Results.Json.Tests/Tests.DeserializeErrorResultOfUnit.verified.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[ | ||
{ | ||
Type: MyError, | ||
Message: MyError, | ||
Data: { | ||
Code: { | ||
ValueKind: Number | ||
}, | ||
Description: { | ||
ValueKind: String | ||
} | ||
} | ||
} | ||
] |
13 changes: 13 additions & 0 deletions
13
DotNetThoughts.Results.Json.Tests/Tests.SerializeErrorResultOfUnit.verified.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"Success": false, | ||
"Errors": [ | ||
{ | ||
"Type": "MyError", | ||
"Message": "MyError", | ||
"Data": { | ||
"Code": 123, | ||
"Description": "hej fel" | ||
} | ||
} | ||
] | ||
} |
22 changes: 22 additions & 0 deletions
22
DotNetThoughts.Results.Json.Tests/Tests.SerializeSuccessfulResultOfComplexType.verified.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"Success": true, | ||
"Value": { | ||
"Id": 123, | ||
"Name": "Complex name", | ||
"Ints": [ | ||
1, | ||
2, | ||
3 | ||
], | ||
"ComplexTypes": [ | ||
{ | ||
"Id": 2345, | ||
"Description": "Description" | ||
}, | ||
{ | ||
"Id": 2345, | ||
"Description": "Description" | ||
} | ||
] | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
DotNetThoughts.Results.Json.Tests/Tests.SerializeSuccessfulResultOfPrimitive.verified.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"Success": true, | ||
"Value": 12 | ||
} |
3 changes: 3 additions & 0 deletions
3
DotNetThoughts.Results.Json.Tests/Tests.SerializeSuccessfulResultOfUnit.verified.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"Success": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using FluentAssertions; | ||
|
||
using System.Text.Json; | ||
|
||
|
||
namespace DotNetThoughts.Results.Json.Tests; | ||
|
||
public class Tests | ||
{ | ||
private static JsonSerializerOptions Options => new() | ||
{ | ||
WriteIndented = true, | ||
Converters = { new JsonConverterFactoryForResultOfT() } | ||
}; | ||
|
||
[Fact] | ||
public async Task SerializeSuccessfulResultOfUnit() | ||
{ | ||
var unitResult = UnitResult.Ok; | ||
var json = JsonSerializer.Serialize(unitResult, Options); | ||
await Verify(json); | ||
} | ||
|
||
[Fact] | ||
public async Task SerializeSuccessfulResultOfPrimitive() | ||
{ | ||
var primitiveResult = Result<int>.Ok(12); | ||
var json = JsonSerializer.Serialize(primitiveResult, Options); | ||
await Verify(json); | ||
} | ||
|
||
public record ComplexType | ||
{ | ||
public int Id { get; set; } = 123; | ||
public string Name { get; set; } = "Complex name"; | ||
|
||
public List<int> Ints { get; set; } = [1, 2, 3]; | ||
|
||
public List<NestedComplexType> ComplexTypes { get; set; } = [new(), new()]; | ||
|
||
public record NestedComplexType | ||
{ | ||
public int Id { get; set; } = 2345; | ||
public string Description { get; set; } = "Description"; | ||
} | ||
} | ||
[Fact] | ||
public async Task SerializeSuccessfulResultOfComplexType() | ||
{ | ||
|
||
var complextResult = Result<ComplexType>.Ok(new ComplexType()); | ||
var json = JsonSerializer.Serialize(complextResult, Options); | ||
await Verify(json); | ||
} | ||
|
||
[Fact] | ||
public async Task SerializeErrorResultOfUnit() | ||
{ | ||
var unitResult = UnitResult.Error(new MyError(123, "hej fel")); | ||
var json = JsonSerializer.Serialize(unitResult, Options); | ||
await Verify(json); | ||
} | ||
public record MyError(int Code, string Description) : ErrorBase; | ||
|
||
|
||
[Fact] | ||
public void DeserializeSuccessfulResultOfUnit() | ||
{ | ||
var unitResultJson = @"{""success"": true}"; | ||
var unitResult = JsonSerializer.Deserialize<Result<Unit>>(unitResultJson, Options); | ||
|
||
unitResult.Success.Should().BeTrue(); | ||
unitResult.Value.Should().Be(Unit.Instance); | ||
} | ||
|
||
[Fact] | ||
public void DeserializeSuccessfulResultOfPrimitive() | ||
{ | ||
var unitResultJson = @"{""success"": true, ""value"": 123 }"; | ||
var unitResult = JsonSerializer.Deserialize<Result<int>>(unitResultJson, Options); | ||
|
||
unitResult.Success.Should().BeTrue(); | ||
unitResult.Value.Should().Be(123); | ||
} | ||
|
||
[Fact] | ||
public void DeserializeSuccessfulResultOfComplextType() | ||
{ | ||
var complextResult = Result<ComplexType>.Ok(new ComplexType()); | ||
var json = JsonSerializer.Serialize(complextResult, Options); | ||
var deserialized = JsonSerializer.Deserialize<Result<ComplexType>>(json, Options); | ||
deserialized.Success.Should().BeTrue(); | ||
deserialized.Value.Should().BeEquivalentTo(new ComplexType()); | ||
} | ||
|
||
[Fact] | ||
public async Task DeserializeErrorResultOfUnit() | ||
{ | ||
var unitResult = UnitResult.Error(new MyError(123, "hej fel")); | ||
var json = JsonSerializer.Serialize(unitResult, Options); | ||
var deserialized = JsonSerializer.Deserialize<Result<Unit>>(json, Options); | ||
deserialized.Success.Should().BeFalse(); | ||
await Verify(deserialized.Errors); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
namespace DotNetThoughts.Results.Json; | ||
|
||
public class DeserializedError : IError | ||
{ | ||
public required string Type { get; init; } | ||
public required string Message { get; init; } | ||
public required Dictionary<string, object?> Data { get; init; } = []; | ||
|
||
public Dictionary<string, object?> GetData() => Data; | ||
} |
15 changes: 15 additions & 0 deletions
15
DotNetThoughts.Results.Json/DotNetThoughts.Results.Json.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<Version>1.5.7</Version> | ||
<GenerateDocumentationFile>True</GenerateDocumentationFile> | ||
<PackageLicenseExpression>MIT</PackageLicenseExpression> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Results\DotNetThoughts.Results.csproj" /> | ||
</ItemGroup> | ||
</Project> |
47 changes: 47 additions & 0 deletions
47
DotNetThoughts.Results.Json/JsonConverterFactoryForResultOfT.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System.Reflection; | ||
using System.Text.Json.Serialization; | ||
using System.Text.Json; | ||
|
||
namespace DotNetThoughts.Results.Json; | ||
|
||
/// <summary> | ||
/// Creates converters for Result<typeparamref name="T"/> and Results of Unit. | ||
/// Hardcoded to not be case sensitive. | ||
/// Serializes the result as an object with a success-property and either a value-property or an errors-property. | ||
/// Value-property is ommitted if T is Unit, or if success is false. | ||
/// | ||
/// Feel free to add customization options and figure out how to honor JsonSerializationOptions like PropertyNameCaseSensitive. | ||
/// </summary> | ||
public class JsonConverterFactoryForResultOfT : JsonConverterFactory | ||
{ | ||
public override bool CanConvert(Type typeToConvert) | ||
=> typeToConvert.IsGenericType | ||
&& typeToConvert.GetGenericTypeDefinition() == typeof(Result<>); | ||
|
||
public override JsonConverter CreateConverter( | ||
Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
|
||
Type elementType = typeToConvert.GetGenericArguments()[0]; | ||
if (typeof(Unit) == elementType) | ||
{ | ||
var converter = (JsonConverter)Activator.CreateInstance( | ||
typeof(JsonConverterForResultOfUnit), | ||
BindingFlags.Instance | BindingFlags.Public, | ||
binder: null, | ||
args: null, | ||
culture: null)!; | ||
return converter; | ||
} | ||
else | ||
{ | ||
var converter = (JsonConverter)Activator.CreateInstance( | ||
typeof(JsonConverterForResultOfT<>).MakeGenericType([elementType]), | ||
BindingFlags.Instance | BindingFlags.Public, | ||
binder: null, | ||
args: null, | ||
culture: null)!; | ||
return converter; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
using System.Text.Json.Serialization; | ||
using System.Text.Json; | ||
|
||
namespace DotNetThoughts.Results.Json; | ||
|
||
internal class JsonConverterForResultOfT<T> : JsonConverter<Result<T>> | ||
{ | ||
public override Result<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
if (reader.TokenType != JsonTokenType.StartObject) | ||
{ | ||
throw new JsonException(); | ||
} | ||
|
||
T? t = default; | ||
bool success = false; | ||
List<DeserializedError> errors = []; | ||
while (reader.Read()) | ||
{ | ||
if (reader.TokenType == JsonTokenType.EndObject) | ||
{ | ||
return success | ||
? Result<T>.Ok(t!) | ||
: Result<T>.Error(errors); | ||
} | ||
if (reader.TokenType != JsonTokenType.PropertyName) | ||
{ | ||
throw new JsonException(); | ||
} | ||
string propertyName = reader.GetString()!.ToLower(); | ||
if (propertyName == nameof(Result<T>.Success).ToLower()) | ||
{ | ||
reader.Read(); | ||
success = reader.GetBoolean(); | ||
} | ||
else if (propertyName == nameof(Result<T>.Value).ToLower()) | ||
{ | ||
t = JsonSerializer.Deserialize<T>(ref reader, options); | ||
} | ||
else if (propertyName == nameof(Result<T>.Errors).ToLower()) | ||
{ | ||
errors = JsonSerializer.Deserialize<List<DeserializedError>>(ref reader, options)!; | ||
} | ||
} | ||
throw new JsonException(); | ||
|
||
} | ||
|
||
public override void Write(Utf8JsonWriter writer, Result<T> value, JsonSerializerOptions options) | ||
{ | ||
if (value.Success) | ||
{ | ||
JsonSerializer.Serialize(writer, new | ||
{ | ||
value.Success, | ||
value.Value | ||
}, options); | ||
} | ||
else | ||
{ | ||
JsonSerializer.Serialize(writer, new | ||
{ | ||
value.Success, | ||
Errors = value.Errors.Select(x => new { x.Type, x.Message, Data = x.GetData() }) | ||
}, options); | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.