Skip to content

Commit

Permalink
fix: Support empty string serialization / deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
HofmeisterAn committed Nov 29, 2024
1 parent e036666 commit 52399a3
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 17 deletions.
30 changes: 30 additions & 0 deletions Docker.DotNet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docker.DotNet.X509", "src\D
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Docker.DotNet.Tests", "test\Docker.DotNet.Tests\Docker.DotNet.Tests.csproj", "{248C5D51-2B33-4A06-A0EA-AA709F752E52}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Docker.DotNet.JsonSerializer8.Tests", "test\Docker.DotNet.JsonSerializer8.Tests\Docker.DotNet.JsonSerializer8.Tests.csproj", "{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Docker.DotNet.JsonSerializer9.Tests", "test\Docker.DotNet.JsonSerializer9.Tests\Docker.DotNet.JsonSerializer9.Tests.csproj", "{F3F00DC7-2C56-4483-B97C-91940DF15178}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -73,6 +77,30 @@ Global
{248C5D51-2B33-4A06-A0EA-AA709F752E52}.Release|x64.Build.0 = Release|Any CPU
{248C5D51-2B33-4A06-A0EA-AA709F752E52}.Release|x86.ActiveCfg = Release|Any CPU
{248C5D51-2B33-4A06-A0EA-AA709F752E52}.Release|x86.Build.0 = Release|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Debug|x64.ActiveCfg = Debug|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Debug|x64.Build.0 = Debug|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Debug|x86.ActiveCfg = Debug|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Debug|x86.Build.0 = Debug|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Release|Any CPU.Build.0 = Release|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Release|x64.ActiveCfg = Release|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Release|x64.Build.0 = Release|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Release|x86.ActiveCfg = Release|Any CPU
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676}.Release|x86.Build.0 = Release|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Debug|x64.ActiveCfg = Debug|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Debug|x64.Build.0 = Debug|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Debug|x86.ActiveCfg = Debug|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Debug|x86.Build.0 = Debug|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Release|Any CPU.Build.0 = Release|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Release|x64.ActiveCfg = Release|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Release|x64.Build.0 = Release|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Release|x86.ActiveCfg = Release|Any CPU
{F3F00DC7-2C56-4483-B97C-91940DF15178}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -82,5 +110,7 @@ Global
{E1F24B25-E027-45E0-A6E1-E08138F1F95D} = {85990620-78A6-4381-8BD6-84E6D0CF0649}
{89BD76AD-78C9-4E4A-96A2-E5DA6D4AFA44} = {85990620-78A6-4381-8BD6-84E6D0CF0649}
{248C5D51-2B33-4A06-A0EA-AA709F752E52} = {AA4B8CC2-1431-4FC7-9DF3-533EC6C86D3A}
{51D89ABF-A08A-4CE8-8AEA-DA65FEDA8676} = {AA4B8CC2-1431-4FC7-9DF3-533EC6C86D3A}
{F3F00DC7-2C56-4483-B97C-91940DF15178} = {AA4B8CC2-1431-4FC7-9DF3-533EC6C86D3A}
EndGlobalSection
EndGlobal
40 changes: 24 additions & 16 deletions src/Docker.DotNet/JsonEnumMemberConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,35 @@
using System.Text.Json;
using System.Text.Json.Serialization;

// https://github.com/dotnet/runtime/issues/74385#issuecomment-1705083109.
internal sealed class JsonEnumMemberConverter<TEnum> : JsonStringEnumConverter<TEnum> where TEnum : struct, Enum
internal sealed class JsonEnumMemberConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
{
public JsonEnumMemberConverter() : base(ResolveNamingPolicy())
{
}
private readonly IReadOnlyDictionary<string, string> _map = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
.Select(field => (Name: field.Name, Attribute: field.GetCustomAttribute<EnumMemberAttribute>()))
.Where(item => item.Attribute != null && item.Attribute.Value != null)
.ToDictionary(item => item.Name, item => item.Attribute.Value, StringComparer.OrdinalIgnoreCase);

private static JsonNamingPolicy ResolveNamingPolicy()
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new EnumMemberNamingPolicy(typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
.Select(fieldInfo => new KeyValuePair<string, string>(fieldInfo.Name, fieldInfo.GetCustomAttribute<EnumMemberAttribute>()?.Value))
.Where(kvp => kvp.Value != null)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
}
var stringValue = reader.GetString();

private sealed class EnumMemberNamingPolicy : JsonNamingPolicy
{
private readonly IReadOnlyDictionary<string, string> _map;
var enumValue = _map.SingleOrDefault(item => item.Value.Equals(stringValue, StringComparison.OrdinalIgnoreCase));

public EnumMemberNamingPolicy(IReadOnlyDictionary<string, string> map) => _map = map;
if (enumValue.Key == null)
{
throw new JsonException($"Unknown enum value '{stringValue}' for enum type '{typeof(TEnum).Name}'.");
}

public override string ConvertName(string name) => _map.TryGetValue(name, out var newName) ? newName : name;
if (!Enum.TryParse(enumValue.Key, out TEnum result))
{
throw new JsonException($"Unable to convert '{stringValue}' to a valid enum value of type '{typeof(TEnum).Name}'.");
}

return result;
}

public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
{
var enumName = value.ToString();
writer.WriteStringValue(_map.TryGetValue(enumName, out var stringValue) ? stringValue : enumName);
}
}
2 changes: 2 additions & 0 deletions src/Docker.DotNet/Properties/InternalVisibleTo.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Docker.DotNet.JsonSerializer8.Tests" + StrongNamePublicKeys.DockerDotNetPublicKey)]
[assembly: InternalsVisibleTo("Docker.DotNet.JsonSerializer9.Tests" + StrongNamePublicKeys.DockerDotNetPublicKey)]
[assembly: InternalsVisibleTo("Docker.DotNet.Tests" + StrongNamePublicKeys.DockerDotNetPublicKey)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="xunit" Version="2.9.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Docker.DotNet\Docker.DotNet.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Docker.DotNet.JsonSerializer8.Tests;

public sealed class JsonEnumMemberConverterTests
{
[Theory]
[ClassData(typeof(RestartPolicyKindTestData))]
public void JsonSerialization_ShouldSerializeAndDeserializeCorrectly(RestartPolicyKind restartPolicyKind)
{
// Given
var parameters = new CreateContainerParameters
{
HostConfig = new HostConfig
{
RestartPolicy = new RestartPolicy
{
Name = restartPolicyKind
}
}
};

// When
var jsonString = JsonSerializer.Instance.Serialize(parameters);
var deserializedParameters = JsonSerializer.Instance.Deserialize<CreateContainerParameters>(Encoding.UTF8.GetBytes(jsonString));

// Then
Assert.Equal(restartPolicyKind, deserializedParameters.HostConfig.RestartPolicy.Name);
}

private sealed class RestartPolicyKindTestData : TheoryData<RestartPolicyKind>
{
public RestartPolicyKindTestData()
{
Add(RestartPolicyKind.Undefined);
Add(RestartPolicyKind.No);
Add(RestartPolicyKind.Always);
Add(RestartPolicyKind.OnFailure);
Add(RestartPolicyKind.UnlessStopped);
}
}
}
3 changes: 3 additions & 0 deletions test/Docker.DotNet.JsonSerializer8.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
global using System.Text;
global using Docker.DotNet.Models;
global using Xunit;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="System.IO.Pipelines" Version="9.0.0" />
<PackageReference Include="System.Net.Http.Json" Version="9.0.0" />
<PackageReference Include="System.Text.Json" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Docker.DotNet\Docker.DotNet.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Docker.DotNet.JsonSerializer8.Tests\*.cs"/>
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ await _dockerClient.Images.DeleteImageAsync(

await cts.CancelAsync();

await Assert.ThrowsAsync<OperationCanceledException>(() => task).ConfigureAwait(false);
await Assert.ThrowsAsync<OperationCanceledException>(() => task);

Assert.True(wasProgressCalled);
}
Expand Down

0 comments on commit 52399a3

Please sign in to comment.