using System; using System.Collections.Generic; using System.Runtime.Serialization; using Newtonsoft.Json; namespace MLEM.Data.Json { /// /// A json type-safe wrapper can be used to wrap any objects that have a custom which stores them as a primitive type and that are serialized using a in cases where is not set to . /// If these objects are not wrapped in this manner, the value deserialized from it might not have the same type as the originally serialized object. This behavior can be observed, for example, when serializing a of entries, one of which is a : The will be serialized as a and, upon deserialization, will remain a . /// In general, wrapping objects in this manner is only useful in rare cases, where custom data of an unexpected or unknown type is stored. /// See for an example of how this class can be used, and see this stackoverflow answer for more information on the problem that this class solves: https://stackoverflow.com/a/38798114. /// public abstract class JsonTypeSafeWrapper { /// /// Returns this json type-safe wrapper's value as an . /// public abstract object Value { get; } /// /// Returns the current of this , typecast to the given type . /// If this 's type is incompatible with the given type, the type's default value is returned instead. /// /// The type of value to return /// The , castt to the given type if compatible, otherwise default public T GetValue() { return this.Value is T t ? t : default; } /// /// Creates a new from the given value. /// The type parameter of the returned wrapper will be equal to the of the passed. /// If a for a specific type, known at comepile type, should be created, you can use . /// /// The value to wrap /// A with a type matching the type of public static JsonTypeSafeWrapper Of(object value) { var type = typeof(JsonTypeSafeWrapper<>).MakeGenericType(value.GetType()); return (JsonTypeSafeWrapper) Activator.CreateInstance(type, value); } } /// [DataContract] public class JsonTypeSafeWrapper : JsonTypeSafeWrapper { /// public override object Value => this.value; [DataMember] private readonly T value; /// /// Creates a new json type-safe wrapper instance that wraps the given . /// If the type of the value is unknown at compile time, can be used instead. /// /// The value to wrap public JsonTypeSafeWrapper(T value) { this.value = value; } } }