From c8d5bd4c51c3970bda624fd69a5105801ea1c8b0 Mon Sep 17 00:00:00 2001
From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com>
Date: Sat, 30 Nov 2024 10:52:29 +0100
Subject: [PATCH] fix: Support empty string enum serialization and
deserialization (#8)
---
.github/workflows/ci.yml | 11 +++--
.github/workflows/publish.yml | 2 +-
src/Directory.Build.props | 2 +-
src/Docker.DotNet/Docker.DotNet.csproj | 5 ++-
src/Docker.DotNet/JsonEnumMemberConverter.cs | 42 ++++++++++++------
src/Docker.DotNet/JsonSerializer.cs | 2 +-
.../Docker.DotNet.Tests.csproj | 2 +-
.../ISystemOperations.Tests.cs | 2 +-
.../JsonEnumMemberConverterTest.cs | 44 +++++++++++++++++++
version.json | 2 +-
10 files changed, 90 insertions(+), 24 deletions(-)
create mode 100644 test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b5d3f49e..301c59f0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,6 +8,11 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
+ strategy:
+ matrix:
+ framework:
+ - net8.0
+ - net9.0
steps:
- uses: actions/checkout@v4
with:
@@ -15,8 +20,8 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.x
+ dotnet-version: 9.x
- name: Build
- run: dotnet build -c Release
+ run: dotnet build -c Release --framework ${{ matrix.framework }}
- name: Test
- run: dotnet test -c Release --no-build
+ run: dotnet test -c Release --framework ${{ matrix.framework }} --no-build
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index efb71227..f6e7330d 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -18,7 +18,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.x
+ dotnet-version: 9.x
- name: Install NBGV tool
run: dotnet tool install --tool-path . nbgv
- name: Set Version
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index c6ac8527..7a0e7a11 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -4,7 +4,7 @@
true
- net6.0;net8.0;netstandard2.0;netstandard2.1
+ net8.0;net9.0;netstandard2.0;netstandard2.1
https://camo.githubusercontent.com/fa6d5c12609ed8a3ba1163b96f9e9979b8f59b0d/687474703a2f2f7765732e696f2f566663732f636f6e74656e74
Copyright (c) .NET Foundation and Contributors
Docker Container C# .NET
diff --git a/src/Docker.DotNet/Docker.DotNet.csproj b/src/Docker.DotNet/Docker.DotNet.csproj
index 07a06a97..a380a0ae 100644
--- a/src/Docker.DotNet/Docker.DotNet.csproj
+++ b/src/Docker.DotNet/Docker.DotNet.csproj
@@ -5,7 +5,10 @@
Docker.DotNet
latest
-
+
+
+
+
diff --git a/src/Docker.DotNet/JsonEnumMemberConverter.cs b/src/Docker.DotNet/JsonEnumMemberConverter.cs
index 0b8f9aff..7b56dd2e 100644
--- a/src/Docker.DotNet/JsonEnumMemberConverter.cs
+++ b/src/Docker.DotNet/JsonEnumMemberConverter.cs
@@ -8,27 +8,41 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-// https://github.com/dotnet/runtime/issues/74385#issuecomment-1705083109.
-internal sealed class JsonEnumMemberConverter : JsonStringEnumConverter where TEnum : struct, Enum
+internal sealed class JsonEnumMemberConverter : JsonConverter where TEnum : struct, Enum
{
- public JsonEnumMemberConverter() : base(ResolveNamingPolicy())
- {
- }
+ private readonly Dictionary _enumFields = typeof(TEnum).GetFields(BindingFlags.Public | BindingFlags.Static)
+ .Select(field => (Name: field.Name, Attribute: field.GetCustomAttribute()))
+ .Where(item => item.Attribute != null && item.Attribute.Value != null)
+ .ToDictionary(item => item.Name, item => item.Attribute.Value);
- 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(fieldInfo.Name, fieldInfo.GetCustomAttribute()?.Value))
- .Where(kvp => kvp.Value != null)
- .ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
+ var stringValue = reader.GetString();
+
+ var enumField = _enumFields.SingleOrDefault(item => item.Value.Equals(stringValue, StringComparison.Ordinal));
+
+ if (enumField.Key == null)
+ {
+ throw new JsonException($"Unknown enum value '{stringValue}' for enum type '{typeof(TEnum).Name}'.");
+ }
+
+ if (!Enum.TryParse(enumField.Key, out TEnum enumValue))
+ {
+ throw new JsonException($"Unable to convert '{stringValue}' to a valid enum value of type '{typeof(TEnum).Name}'.");
+ }
+
+ return enumValue;
}
- private sealed class EnumMemberNamingPolicy : JsonNamingPolicy
+ public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
{
- private readonly IReadOnlyDictionary _map;
+ var enumName = value.ToString();
- public EnumMemberNamingPolicy(IReadOnlyDictionary map) => _map = map;
+ if (!_enumFields.TryGetValue(enumName, out var stringValue))
+ {
+ throw new JsonException($"Unable to convert '{enumName}' to a valid enum value of type '{nameof(String)}'.");
+ }
- public override string ConvertName(string name) => _map.TryGetValue(name, out var newName) ? newName : name;
+ writer.WriteStringValue(stringValue);
}
}
\ No newline at end of file
diff --git a/src/Docker.DotNet/JsonSerializer.cs b/src/Docker.DotNet/JsonSerializer.cs
index e64dde62..033c6144 100644
--- a/src/Docker.DotNet/JsonSerializer.cs
+++ b/src/Docker.DotNet/JsonSerializer.cs
@@ -18,8 +18,8 @@ internal sealed class JsonSerializer
private JsonSerializer()
{
- _options.Converters.Add(new JsonEnumMemberConverter());
_options.Converters.Add(new JsonEnumMemberConverter());
+ _options.Converters.Add(new JsonEnumMemberConverter());
_options.Converters.Add(new JsonDateTimeConverter());
_options.Converters.Add(new JsonNullableDateTimeConverter());
_options.Converters.Add(new JsonBase64Converter());
diff --git a/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj b/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj
index cf8e7333..8408738a 100644
--- a/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj
+++ b/test/Docker.DotNet.Tests/Docker.DotNet.Tests.csproj
@@ -1,6 +1,6 @@
- net8.0
+ net8.0;net9.0
false
false
diff --git a/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs b/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
index d32e0a34..0ad75ebd 100644
--- a/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
+++ b/test/Docker.DotNet.Tests/ISystemOperations.Tests.cs
@@ -118,7 +118,7 @@ await _dockerClient.Images.DeleteImageAsync(
await cts.CancelAsync();
- await Assert.ThrowsAsync(() => task).ConfigureAwait(false);
+ await Assert.ThrowsAsync(() => task);
Assert.True(wasProgressCalled);
}
diff --git a/test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs b/test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs
new file mode 100644
index 00000000..53a001c0
--- /dev/null
+++ b/test/Docker.DotNet.Tests/JsonEnumMemberConverterTest.cs
@@ -0,0 +1,44 @@
+namespace Docker.DotNet.Tests;
+
+using System.Text;
+using Docker.DotNet.Models;
+using Xunit;
+
+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(Encoding.UTF8.GetBytes(jsonString));
+
+ // Then
+ Assert.Equal(restartPolicyKind, deserializedParameters.HostConfig.RestartPolicy.Name);
+ }
+
+ private sealed class RestartPolicyKindTestData : TheoryData
+ {
+ public RestartPolicyKindTestData()
+ {
+ Add(RestartPolicyKind.Undefined);
+ Add(RestartPolicyKind.No);
+ Add(RestartPolicyKind.Always);
+ Add(RestartPolicyKind.OnFailure);
+ Add(RestartPolicyKind.UnlessStopped);
+ }
+ }
+}
\ No newline at end of file
diff --git a/version.json b/version.json
index 165ac236..423da8f4 100644
--- a/version.json
+++ b/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
- "version": "3.126.0",
+ "version": "3.126.1",
"nugetPackageVersion": {
"semVer": 2
},