2015-07-10 106 views
3

system.enum我有我使用的驱动分析引擎的行为,我正在写一个非常通用的“规则”类:反序列化特定枚举到Json.Net

public class Rule 
{ 
    /// <summary> 
    /// The general rule type. 
    /// </summary> 
    public RuleType RuleType { get; set; } 

    /// <summary> 
    /// The human-readable description of the rule. 
    /// </summary> 
    public string RuleDescription { get; set; } 

    /// <summary> 
    /// The integer magnitude of the rule, if applicable. 
    /// </summary> 
    public int? RuleInt { get; set; } 

    /// <summary> 
    /// The boolean sign associated with the rule, if applicable. 
    /// </summary> 
    public bool? RuleBool { get; set; } 

    /// <summary> 
    /// The enum flag associated with the rule, if applicable. CAN be null. 
    /// </summary> 
    public System.Enum RuleFlagEnum { get; set; } 

    /// <summary> 
    /// A dumping ground for any other random crap I've failed to account for at this point in time. 
    /// </summary> 
    public object RuleObject { get; set; } 
} 

RuleType是具体的枚举,如下所示:

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

使用Json.NET,这两个序列化和反序列化就好了。

然而,RuleEnum给我带来了问题。无论使用默认的枚举序列化还是字符串枚举序列化,都不提供枚举的特定类型。因此,在反序列化过程中,我留下了System.Enum和一个字符串值,这完全没有帮助。

这是序列化的一个例子,显示什么我谈论:

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

RuleFlagEnum,在这种情况下,指的是枚举:

public enum SignChange 
{ 
    Zero, 
    Positive, 
    Negative 
} 

我曾尝试使用Json.NET中的所有TypeNameHandling选项。它们只在对象上放置类型提示,这对RuleFlagEnum没有帮助,因为它在技术上是原始的。

我真的很想在System.Enum中保留枚举,这样我们就可以加载任何枚举以供规则类型以后的解释使用,因此整个事物更具可扩展性。这可能吗?

+0

您的意思是说您希望输出为“”RuleFlagEnum:“SignChange.Negative”'而不是? – cubrr

+0

@cubrr:如果这意味着Json.NET可以正确反序列化它,而无需编写完全自定义的反序列化处理程序,那么是的! – tmesser

+0

@OrelEraki:如果你阅读这个问题,这绝对不是。我已经将枚举序列化为一个字符串。我的问题是从该字符串开始的反序列化作为对象层次结构的一部分,但是没有足够的关键数据以使其正常工作。 – tmesser

回答

2

的这里的困难是System.Enum是一个抽象类,所以是不可能的反序列化未知混凝土类型的值作为这样的类型。相反,我们需要在某个地方的JSON中具有特定的类型信息,但是Json.NET会将一个enum作为一个字符串或一个整数(取决于是否应用了一个StringEnumConverter)而不是一个对象,从而不会将其作为一个对象添加polymorphic "$type" property的机会。

的解决方案是,序列化时,序列化的通用包装类,可以传达的具体类型的信息:

public abstract class TypeWrapper 
{ 
    protected TypeWrapper() { } 

    [JsonIgnore] 
    public abstract object ObjectValue { get; } 

    public static TypeWrapper CreateWrapper<T>(T value) 
    { 
     if (value == null) 
      return new TypeWrapper<T>(); 
     var type = value.GetType(); 
     if (type == typeof(T)) 
      return new TypeWrapper<T>(value); 
     // Return actual type of subclass 
     return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value); 
    } 
} 

public sealed class TypeWrapper<T> : TypeWrapper 
{ 
    public TypeWrapper() : base() { } 

    public TypeWrapper(T value) 
     : base() 
    { 
     this.Value = value; 
    } 

    public override object ObjectValue { get { return Value; } } 

    public T Value { get; set; } 
} 

然后使用序列化的类时序列化包装:

/// <summary> 
    /// The enum flag associated with the rule, if applicable. CAN be null. 
    /// </summary> 
    [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; 
     } 
    } 

这产生JSON如下:

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