From 407cf5d0bf9d3563ae77fd34ce29ffae5af4339f Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Tue, 5 Mar 2024 00:44:54 +0100 Subject: Add MediaStreamProtocol enum (#10153) * Add MediaStreamProtocol enum * Add default handling for enum during deserialization --------- Co-authored-by: Cody Robibero --- .../Converters/JsonDefaultStringEnumConverter.cs | 49 ++++++++++++++++++++++ .../JsonDefaultStringEnumConverterFactory.cs | 31 ++++++++++++++ .../Json/Converters/JsonGuidConverter.cs | 2 +- .../Json/Converters/JsonNullableStructConverter.cs | 5 +-- src/Jellyfin.Extensions/Json/JsonDefaults.cs | 1 + src/Jellyfin.Extensions/Json/Utf8JsonExtensions.cs | 27 ++++++++++++ 6 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverterFactory.cs create mode 100644 src/Jellyfin.Extensions/Json/Utf8JsonExtensions.cs (limited to 'src') diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverter.cs new file mode 100644 index 000000000..06ecfc558 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverter.cs @@ -0,0 +1,49 @@ +using System; +using System.ComponentModel; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters; + +/// +/// Json unknown enum converter. +/// +/// The type of enum. +public class JsonDefaultStringEnumConverter : JsonConverter + where T : struct, Enum +{ + private readonly JsonConverter _baseConverter; + + /// + /// Initializes a new instance of the class. + /// + /// The base json converter. + public JsonDefaultStringEnumConverter(JsonConverter baseConverter) + { + _baseConverter = baseConverter; + } + + /// + public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.IsNull() || reader.IsEmptyString()) + { + var customValueAttribute = typeToConvert.GetCustomAttribute(); + if (customValueAttribute?.Value is null) + { + throw new InvalidOperationException($"Default value not set for '{typeToConvert.Name}'"); + } + + return (T)customValueAttribute.Value; + } + + return _baseConverter.Read(ref reader, typeToConvert, options); + } + + /// + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + _baseConverter.Write(writer, value, options); + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverterFactory.cs new file mode 100644 index 000000000..5a9bf546e --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonDefaultStringEnumConverterFactory.cs @@ -0,0 +1,31 @@ +using System; +using System.ComponentModel; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters; + +/// +/// Utilizes the JsonStringEnumConverter and sets a default value if not provided. +/// +public class JsonDefaultStringEnumConverterFactory : JsonConverterFactory +{ + private static readonly JsonStringEnumConverter _baseConverterFactory = new(); + + /// + public override bool CanConvert(Type typeToConvert) + { + return _baseConverterFactory.CanConvert(typeToConvert) + && typeToConvert.IsDefined(typeof(DefaultValueAttribute)); + } + + /// + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var baseConverter = _baseConverterFactory.CreateConverter(typeToConvert, options); + var converterType = typeof(JsonDefaultStringEnumConverter<>).MakeGenericType(typeToConvert); + + return (JsonConverter?)Activator.CreateInstance(converterType, baseConverter); + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs index ea6d141cb..2964c6943 100644 --- a/src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs +++ b/src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs @@ -12,7 +12,7 @@ namespace Jellyfin.Extensions.Json.Converters { /// public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => reader.TokenType == JsonTokenType.Null + => reader.IsNull() ? Guid.Empty : ReadInternal(ref reader); diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs index 28437023f..94004fa49 100644 --- a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs +++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs @@ -15,10 +15,7 @@ namespace Jellyfin.Extensions.Json.Converters /// public override TStruct? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - // Token is empty string. - if (reader.TokenType == JsonTokenType.String - && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) - || (!reader.HasValueSequence && reader.ValueSpan.IsEmpty))) + if (reader.IsEmptyString()) { return null; } diff --git a/src/Jellyfin.Extensions/Json/JsonDefaults.cs b/src/Jellyfin.Extensions/Json/JsonDefaults.cs index 9e6d4c3f8..cbe5849ec 100644 --- a/src/Jellyfin.Extensions/Json/JsonDefaults.cs +++ b/src/Jellyfin.Extensions/Json/JsonDefaults.cs @@ -38,6 +38,7 @@ namespace Jellyfin.Extensions.Json new JsonNullableGuidConverter(), new JsonVersionConverter(), new JsonFlagEnumConverterFactory(), + new JsonDefaultStringEnumConverterFactory(), new JsonStringEnumConverter(), new JsonNullableStructConverterFactory(), new JsonDateTimeConverter(), diff --git a/src/Jellyfin.Extensions/Json/Utf8JsonExtensions.cs b/src/Jellyfin.Extensions/Json/Utf8JsonExtensions.cs new file mode 100644 index 000000000..d06508a26 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Utf8JsonExtensions.cs @@ -0,0 +1,27 @@ +using System.Text.Json; + +namespace Jellyfin.Extensions.Json; + +/// +/// Extensions for Utf8JsonReader and Utf8JsonWriter. +/// +public static class Utf8JsonExtensions +{ + /// + /// Determines if the reader contains an empty string. + /// + /// The reader. + /// Whether the reader contains an empty string. + public static bool IsEmptyString(this Utf8JsonReader reader) + => reader.TokenType == JsonTokenType.String + && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) + || (!reader.HasValueSequence && reader.ValueSpan.IsEmpty)); + + /// + /// Determines if the reader contains a null value. + /// + /// The reader. + /// Whether the reader contains null. + public static bool IsNull(this Utf8JsonReader reader) + => reader.TokenType == JsonTokenType.Null; +} -- cgit v1.2.3