using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Newtonsoft.Json; #if NET6_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif namespace MLEM.Data.Json { /// /// A that doesn't actually serialize the object, but instead serializes the name given to it by the underlying . /// Optionally, the name of a can be passed to this converter when used in the by passing the arguments for the constructor as . /// /// The type of the object to convert public class StaticJsonConverter : JsonConverter { private readonly Dictionary entries; private readonly Dictionary inverse; /// /// Creates a new static json converter using the given underlying . /// /// The dictionary to use public StaticJsonConverter(Dictionary entries) { this.entries = entries; this.inverse = entries.ToDictionary(kv => kv.Value, kv => kv.Key); } /// /// Creates a new static json converter by finding the underlying from the given type and member name /// /// The type that the dictionary is declared in /// The name of the dictionary itself public StaticJsonConverter( #if NET6_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] #endif Type type, string memberName) : this(StaticJsonConverter.GetEntries(type, memberName)) {} /// Writes the JSON representation of the object. /// The to write to. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer) { if (!this.inverse.TryGetValue(value, out var key)) throw new InvalidOperationException($"Cannot write {value} that is not a registered entry"); writer.WriteValue(key); } /// Reads the JSON representation of the object. /// The to read from. /// Type of the object. /// The existing value of object being read. If there is no existing value then null will be used. /// The existing value has a value. /// The calling serializer. /// The object value. public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer) { var val = reader.Value?.ToString(); if (val == null) return default; this.entries.TryGetValue(val, out var ret); return ret; } private static Dictionary GetEntries( #if NET6_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] #endif Type type, string memberName) { const BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; var value = type.GetProperty(memberName, flags)?.GetValue(null) ?? type.GetField(memberName, flags)?.GetValue(null); if (value == null) throw new ArgumentException($"There is no property or field value for name {memberName}", nameof(memberName)); return value as Dictionary ?? throw new InvalidCastException($"{value} is not of expected type {typeof(T)}"); } } }