Deserializar enumeración específica en system.enum en Json.Net

Tengo una clase de ‘reglas’ bastante genérica que estoy usando para conducir el comportamiento de un motor de análisis que estoy escribiendo:

public class Rule { ///  /// The general rule type. ///  public RuleType RuleType { get; set; } ///  /// The human-readable description of the rule. ///  public string RuleDescription { get; set; } ///  /// The integer magnitude of the rule, if applicable. ///  public int? RuleInt { get; set; } ///  /// The boolean sign associated with the rule, if applicable. ///  public bool? RuleBool { get; set; } ///  /// The enum flag associated with the rule, if applicable. CAN be null. ///  public System.Enum RuleFlagEnum { get; set; } ///  /// A dumping ground for any other random crap I've failed to account for at this point in time. ///  public object RuleObject { get; set; } } 

RuleType es una enumeración específica, así:

 public enum RuleType { Invalid, ModifyDifficulty, StrengthChange, ColorChange, SignChange } 

Usando Json.NET, eso serializa y deserializa muy bien.

RuleEnum, sin embargo, me está dando problemas. Ya sea que se use la serialización enum predeterminada o la serialización enum de cadena, no se proporciona el tipo específico de enumeración. Como tal, durante la deserialización, me queda System.Enum y un valor de cadena, que es completamente inútil.

Este es un ejemplo de la serialización, para mostrar de lo que estoy hablando:

 { "RuleType": "SignChange", "RuleDescription": "Strength 1 Inversion Gate", "RuleInt": 1, "RuleFlagEnum": "Negative" } 

RuleFlagEnum, en este caso, se refiere a la enumeración:

 public enum SignChange { Zero, Positive, Negative } 

He intentado usar todas las opciones de TypeNameHandling dentro de Json.NET. Solo colocan sugerencias sobre los objetos, lo que no ayuda con RuleFlagEnum ya que técnicamente es una primitiva.

Realmente, me gustaría mantener la enumeración en System.Enum para que podamos cargar cualquier enumeración arbitraria para su posterior interpretación por el tipo de regla, por lo que todo es más ampliable. es posible?

La dificultad aquí es que System.Enum es una clase abstracta, por lo que es imposible deserializar un valor de tipo concreto desconocido como tal tipo. Por el contrario, uno necesita tener la información de tipo específico en el JSON en alguna parte, sin embargo Json.NET serializará una enum como una cadena o un número entero (dependiendo de si se aplica un StringEnumConverter ), pero no como un objeto, sin dejar ningún oportunidad para agregar una propiedad polimórfica de "$type" .

La solución es, al serializar, serializar una clase contenedora genérica que pueda transmitir la información de tipo concreto:

 public abstract class TypeWrapper { protected TypeWrapper() { } [JsonIgnore] public abstract object ObjectValue { get; } public static TypeWrapper CreateWrapper(T value) { if (value == null) return new TypeWrapper(); var type = value.GetType(); if (type == typeof(T)) return new TypeWrapper(value); // Return actual type of subclass return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value); } } public sealed class TypeWrapper : TypeWrapper { public TypeWrapper() : base() { } public TypeWrapper(T value) : base() { this.Value = value; } public override object ObjectValue { get { return Value; } } public T Value { get; set; } } 

Luego use serializar el contenedor al serializar su clase:

  ///  /// The enum flag associated with the rule, if applicable. CAN be null. ///  [JsonIgnore] public System.Enum RuleFlagEnum { get; set; } [JsonProperty("RuleFlagEnum", TypeNameHandling = TypeNameHandling.All)] TypeWrapper RuleFlagEnumValue { get { return RuleFlagEnum == null ? null : TypeWrapper.CreateWrapper(RuleFlagEnum); } set { if (value == null || value.ObjectValue == null) RuleFlagEnum = null; else RuleFlagEnum = (Enum)value.ObjectValue; } } 

Esto produce JSON como el siguiente:

 { "RuleType": "ModifyDifficulty", "RuleFlagEnum": { "$type": "Question31351262.TypeWrapper`1[[Question31351262.MyEnum, MyApp]], MyApp", "Value": "Two, Three" }, }