问题描述
我有一个相当通用的规则"类,我用它来驱动我正在编写的分析引擎的行为:
公共类规则{///<总结>///通用规则类型.///</总结>公共规则类型规则类型 { 获取;放;}///<总结>///规则的人类可读描述.///</总结>公共字符串 RuleDescription { 获取;放;}///<总结>///如果适用,规则的整数幅度.///</总结>公共诠释?RuleInt { 得到;放;}///<总结>///与规则关联的布尔符号(如果适用).///</总结>公共布尔?RuleBool { 得到;放;}///<总结>///与规则关联的枚举标志(如果适用).可以为空.///</总结>公共 System.Enum RuleFlagEnum { 获取;放;}///<总结>///任何其他随机垃圾的垃圾场,我目前未能及时解释.///</总结>公共对象 RuleObject { 获取;放;}}
RuleType 是一个特定的枚举,如下所示:
公共枚举 RuleType{无效的,修改难度,强度变化,变色,标志改变}
使用 Json.NET,序列化和反序列化都很好.
然而,RuleEnum 给我带来了问题.无论是使用默认枚举序列化还是字符串枚举序列化,都没有提供枚举的具体类型.因此,在反序列化期间,我只剩下 System.Enum
和一个字符串值,这完全没有帮助.
这是一个序列化的例子,来说明我在说什么:
<代码>{"RuleType": "SignChange","RuleDescription": "Strength 1 Inversion Gate",规则":1,RuleFlagEnum":负"}
在这种情况下,RuleFlagEnum 指的是枚举:
公共枚举 SignChange{零,积极的,消极的}
我已尝试使用 Json.NET 中的所有 TypeNameHandling
选项.他们只对对象进行类型提示,这对 RuleFlagEnum 没有帮助,因为它在技术上是一个原语.
我真的非常想将枚举保留在 System.Enum 中,这样我们就可以加载任意枚举,以便以后按规则类型进行解释,这样整个事情就更具可扩展性.这可能吗?
这里的难点在于 System.Enum
是一个抽象类,因此不可能将未知具体类型的值反序列化为这种类型.相反,需要在 JSON 中的某处具有特定类型信息,但是 Json.NET 会将 enum
序列化为字符串或整数(取决于 StringEnumConverter
被应用)——但不是作为对象,因此没有机会对于 多态 "$type"
属性待补充.
解决的办法是,在序列化的时候,序列化一个可以传达具体类型信息的泛型包装类:
公共抽象类 TypeWrapper{受保护的 TypeWrapper() { }[Json忽略]公共抽象对象 ObjectValue { 获取;}public static TypeWrapper CreateWrapper(T值){如果(值 == 空)返回新的 TypeWrapper<T>();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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!