using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.Serialization; using System.Text.Json; using System.Text.Json.Serialization; namespace StabilityMatrix.Core.Converters.Json; public class DefaultUnknownEnumConverter< [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T > : JsonConverter where T : Enum { /// /// Lazy initialization for . /// private readonly Lazy> _enumMemberValuesLazy = new( () => typeof(T) .GetFields() .Where(field => field.IsStatic) .Select( field => new { FieldName = field.Name, FieldValue = (T)field.GetValue(null)!, EnumMemberValue = field .GetCustomAttributes(false) .FirstOrDefault() ?.Value?.ToString() } ) .ToDictionary(x => x.EnumMemberValue ?? x.FieldName, x => x.FieldValue) ); /// /// Gets a dictionary of enum member values, keyed by the EnumMember attribute value, or the field name if no EnumMember attribute is present. /// private Dictionary EnumMemberValues => _enumMemberValuesLazy.Value; /// /// Lazy initialization for . /// private readonly Lazy> _enumMemberNamesLazy; /// /// Gets a dictionary of enum member names, keyed by the enum member value. /// private Dictionary EnumMemberNames => _enumMemberNamesLazy.Value; /// /// Gets the value of the "Unknown" enum member, or the 0 value if no "Unknown" member is present. /// private T UnknownValue => EnumMemberValues.TryGetValue("Unknown", out var res) ? res : (T)Enum.ToObject(typeof(T), 0); /// public override bool HandleNull => true; public DefaultUnknownEnumConverter() { _enumMemberNamesLazy = new Lazy>( () => EnumMemberValues.ToDictionary(x => x.Value, x => x.Key) ); } /// public override T Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) { if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName)) { throw new JsonException("Expected String or PropertyName token"); } if (reader.GetString() is { } readerString) { // First try get exact match if (EnumMemberValues.TryGetValue(readerString, out var enumMemberValue)) { return enumMemberValue; } // Otherwise try get case-insensitive match if ( EnumMemberValues.Keys.FirstOrDefault( key => key.Equals(readerString, StringComparison.OrdinalIgnoreCase) ) is { } enumMemberName ) { return EnumMemberValues[enumMemberName]; } Debug.WriteLine($"Unknown enum member value for {typeToConvert}: {readerString}"); } return UnknownValue; } /// public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) { if (value == null) { writer.WriteNullValue(); return; } writer.WriteStringValue(EnumMemberNames[value]); } /// public override T ReadAsPropertyName( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options ) => Read(ref reader, typeToConvert, options); /// public override void WriteAsPropertyName( Utf8JsonWriter writer, T? value, JsonSerializerOptions options ) => Write(writer, value, options); }