在 Json.Net 中将特定枚举反序列化为 system.enum [英] Deserialize specific enum into system.enum in Json.Net

查看:20
本文介绍了在 Json.Net 中将特定枚举反序列化为 system.enum的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个相当通用的规则"类,我用它来驱动我正在编写的分析引擎的行为:

公共类规则{///<总结>///通用规则类型.///</总结>公共规则类型 RuleType { 获取;放;}///<总结>///人类可读的规则描述.///</总结>公共字符串 RuleDescription { 获取;放;}///<总结>///规则的整数大小,如果适用.///</总结>公众号?RuleInt { 获取;放;}///<总结>///与规则关联的布尔符号(如果适用).///</总结>公共布尔?RuleBool { 获取;放;}///<总结>///与规则关联的枚举标志(如果适用).可以为空.///</总结>公共 System.Enum RuleFlagEnum { 获取;放;}///<总结>///在这个时间点我没有考虑到的任何其他随机垃圾的倾倒场.///</总结>公共对象 RuleObject { 获取;放;}}

RuleType 是一个特定的枚举,如下所示:

公共枚举规则类型{无效的,修改难度,实力变化,颜色变化,符号更改}

使用 Json.NET,序列化和反序列化都很好.

然而,RuleEnum 给我带来了问题.无论是使用默认的枚举序列化还是字符串枚举序列化,都没有提供具体的枚举类型.因此,在反序列化期间,我只剩下 System.Enum 和一个字符串值,这完全没有帮助.

这是序列化的一个例子,以显示我在说什么:

<代码>{"RuleType": "SignChange","RuleDescription": "强度 1 反转门","RuleInt": 1,"RuleFlagEnum": "否定"}

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

公共枚举 SignChange{零,积极的,消极的}

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

我真的,真的很想将枚举保留在 System.Enum 中,这样我们就可以加载任何任意枚举,以便以后由规则类型进行解释,从而使整个事物更具可扩展性.这可能吗?

解决方案

这里的难点在于 System.Enum 是一个抽象类,因此不可能将未知具体类型的值反序列化为这样的类型.相反,需要在 JSON 中的某处具有特定的类型信息,但是 Json.NET 会将 enum 序列化为字符串或整数(取决于 StringEnumConverter 已应用)——但不是作为对象,因此没有机会对于 多态 "$type" 属性待补充.

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

公共抽象类TypeWrapper{受保护的 TypeWrapper() { }[Json忽略]公共抽象对象 ObjectValue { get;}public static TypeWrapper CreateWrapper(T 值){如果(值 == 空)返回新的 TypeWrapper();var type = value.GetType();if (type == typeof(T))返回新的 TypeWrapper(值);//返回子类的实际类型return (TypeWrapper)Activator.CreateInstance(typeof(TypeWrapper<>).MakeGenericType(type), value);}}公共密封类 TypeWrapper<T>: 类型包装器{公共 TypeWrapper() : base() { }公共类型包装器(T 值): 根据(){this.Value = 值;}公共覆盖对象 ObjectValue { 获取 { 返回值;} }公共 T 值 { 得到;放;}}

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

///<摘要>///与规则关联的枚举标志(如果适用).可以为空.///</总结>[Json忽略]公共 System.Enum RuleFlagEnum { 获取;放;}[JsonProperty("RuleFlagEnum", TypeNameHandling = TypeNameHandling.All)]TypeWrapper RuleFlagEnumValue{得到{返回 RuleFlagEnum == null ?null : TypeWrapper.CreateWrapper(RuleFlagEnum);}放{if (value == null || value.ObjectValue == null)RuleFlagEnum = null;别的RuleFlagEnum = (Enum)value.ObjectValue;}}

这会产生如下的 JSON:

<块引用>

<代码>{"RuleType": "修改难度",规则标志枚举":{"$type": "Question31351262.TypeWrapper`1[[Question31351262.MyEnum, MyApp]], MyApp",价值":二、三"},}

I have a fairly generic 'rule' class that I am using to drive the behavior of an analysis engine I'm writing:

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 is a specific enum, like so:

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

Using Json.NET, that both serializes and deserializes just fine.

RuleEnum, however, is giving me problems. Whether using the default enum serialization or the string enum serialization, the specific type of enum is not provided. As such, during deserialization, I am left with System.Enum and a string value, which is wholly unhelpful.

This is an example of the serialization, to show what I'm talking about:

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

RuleFlagEnum, in this case, is referring to the enum:

public enum SignChange
{
    Zero,
    Positive,
    Negative
}

I have tried using all of the TypeNameHandling options inside Json.NET. They only put type hinting on the objects, which doesn't help with RuleFlagEnum since it is technically a primitive.

I would really, really like to keep the enum at System.Enum so we can load any arbitrary enum in for later interpretation by the rule type, so the entire thing is more expandable. Is this possible?

解决方案

The difficulty here is that System.Enum is an abstract class, so it is impossible to deserialize a value of unknown concrete type as such a type. Rather, one needs to have the specific type information in the JSON somewhere, however Json.NET will serialize an enum as a string or an integer (depending upon whether a StringEnumConverter is applied) -- but not an as an object, thus leaving no opportunity for a polymorphic "$type" property to be added.

The solution is, when serializing, to serialize a generic wrapper class that can convey the concrete type information:

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; }
}

Then use serialize the wrapper when serializing your class:

    /// <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;
        }
    }

This produces JSON like the following:

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

这篇关于在 Json.Net 中将特定枚举反序列化为 system.enum的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆