using System.Reflection; using adas_core.Domain.Enums; using MongoDB.Bson; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; namespace adas_core.Domain.Models.MongoModels; /// /// Represents a custom JSON converter for serializing and deserializing historical configuration changes. /// /// /// This converter extends to provide tailored JSON conversion behavior for historical config change data. /// public class HistoricalConfigChanges : JsonConverter { public DateTime Time; public string Username = string.Empty; public ObjectId Id { get; set; } public DisplayConfigEnums.ConfigTypes? ConfigType { get; set; } public string? OldConfig { get; set; } public string NewConfig { get; set; } = string.Empty; /// /// Serializes the specified object to JSON, skipping output when the value is null. /// The object is first projected into a and annotated with a string enum converter for the ConfigType property before being written to the . /// /// The that receives the serialized JSON output. /// The object to serialize. If null, the method does nothing. /// The used during serialization. public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if (value != null) { var jo = JObject.FromObject(value); AddStringEnumConverterAttribute(value, nameof(ConfigType)); jo.WriteTo(writer); } } /// /// Adds a using to the specified property via reflection, but only if the property does not already have a applied. The new attribute is appended to the existing custom attributes array using a non-public field on . /// /// The object instance whose type defines the target property. /// The name of the property to which the converter attribute should be added. private static void AddStringEnumConverterAttribute(object value, string propertyName) { var prop = value.GetType().GetProperty(propertyName); var attr = new JsonConverterAttribute(typeof(StringEnumConverter)); if (prop != null) { var attrs = prop.GetCustomAttributes(false); // Check if the attribute is not already applied if (Array.Find(attrs, a => a is JsonConverterAttribute) == null) { // Create a new array that includes the existing attributes and the new one var newAttrs = new object[attrs.Length + 1]; Array.Copy(attrs, newAttrs, attrs.Length); newAttrs[attrs.Length] = attr; // Use reflection to set the new attributes array var field = typeof(PropertyInfo).GetField("m_customAttributes", BindingFlags.Instance | BindingFlags.NonPublic); field?.SetValue(prop, newAttrs); } } } /// /// Reads the JSON representation of the object and converts it to the target type as part of a custom . This implementation is not yet provided. /// /// The used to read the JSON content. /// The type of the object to deserialize to. /// The existing value of the object being read, or null if no value exists. /// The used to deserialize nested objects. /// The deserialized object value. /// Always thrown because the method has not been implemented. public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } /// /// Determines whether the converter can convert the specified type. The override has not been implemented and always throws a when invoked. /// /// The type to evaluate for convertibility. /// when the converter supports ; otherwise, . /// Thrown in all cases because the method body is not implemented. public override bool CanConvert(Type objectType) { throw new NotImplementedException(); } }