如何强制JSON序列化包含多态System.Enum成员的类型信息



我正在使用JSON序列化和反序列化来序列化/反序列化我的大型应用程序的存储类。在我的应用程序中,我需要在枚举中保存不同enumsType的枚举,或者需要保存从父类继承到父类类型变量的不同类的总和。请看下面的代码,它演示了一个枚举的示例。在这种情况下,序列化会出错,尤其是对于枚举,并且由于枚举中变量的类型而找不到枚举。例如,在下面的示例代码中,inputEnum的类型为Enum,并且可以获得Enum1和Enum2的枚举。但在反序列化中,Json带来了无法将int64转换为Enum类型的错误。现在我需要JSON来保存原始类型的变量,以便对它们进行反序列化。例如在inputenum=Enum1.hi中,要保持为Enum1而不是现在发生的Enum的类型。请给出您的解决方案。

public enum Enum1
{
hi=0,
hello
}
public enum Enum2
{
by = 0,
goodbye
}
public class TUnitClass
{
public string Id { set; get; }
public string Name { set; get; }
public Enum inputenum { set; get; }
public string Comment { set; get; }
public string displayingString { set; get; }

}
private void Form1_Load(object sender, EventArgs e)
{
List<TUnitClass> cls1=new List<TUnitClass>()
{
new TUnitClass() {inputenum = Enum2.goodbye},
new TUnitClass() {inputenum = Enum2.by},
new TUnitClass() {inputenum = Enum1.hello},
new TUnitClass() {inputenum = Enum1.hi},
new TUnitClass() {inputenum = Enum2.goodbye},
};
string strSerialized = JsonConvert.SerializeObject(cls1);
List<TUnitClass> cls2 = JsonConvert.DeserializeObject<List<TUnitClass>>(strSerialized,new JsonSerializerSettings() {Error = ErrorOnDeserialization});
}
private void ErrorOnDeserialization(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs e)
{

}

更新

我试图在Json.Net中实现从反序列化特定枚举到system.enum的解决方案。在序列化过程中,类型信息被正确写入Json文件,但在反序列化过程中会引发异常:

Newtonsoft.Json.JsonSerializationException occurred
Message="Type specified in JSON 'MTC.Utility.UnitConvertor.TypeWrapper`1[[MTC.Utility.UnitConvertor.EAcceleration, MTC.Utility.UnitConvertor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]], MTC.Utility.UnitConvertor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not compatible with 'System.Enum, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. Path 'listOfUnitSystems[0].UnitsList[0].Unit.$type', line 17, position 269."
Source="Newtonsoft.Json"
StackTrace:
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName) in C:DevelopmentReleasesJsonWorkingNewtonsoft.JsonWorking-SignedSrcNewtonsoft.JsonSerializationJsonSerializerInternalReader.cs:line 814
InnerException: 

这是更新后的代码:

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; }
}
public class TUnitClass
{
public string Id { set; get; }
public string Name { set; get; }
public EQuantities Quantity { set; get; }
[JsonIgnore]
public System.Enum Unit { get; set; }
[JsonProperty("Unit", TypeNameHandling = TypeNameHandling.All)]
TypeWrapper UnitValue
{
get
{
return Unit == null ? null : TypeWrapper.CreateWrapper(Unit);
}
set
{
if (value == null || value.ObjectValue == null)
Unit = null;
else
Unit = (Enum)value.ObjectValue;
}
}
public string Comment { set; get; }
public string displayingString { set; get; }
public TUnitClass(string id, EQuantities quantity,Enum unit, string comment)
{
Id = id;
Name = quantity.ToString();
Quantity = quantity;
Unit = unit;
Comment = comment;
FieldInfo fi = unit.GetType().GetField(unit.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
displayingString= attributes[0].Description;
}
}

更新2

最后,如果我有一个Enum[]属性而不是Enum,我该如何处理这个问题?

问题31351262的解决方案不起作用的原因是您的类型TUnitClass没有默认构造函数。相反,它有一个单独的参数化构造函数。在这种情况下,Json.NET将调用该构造函数,通过名称、模大小写将Json对象与构造函数参数匹配。(有关详细信息,请参阅此答案。)不幸的是,您的构造函数将抽象的System.Enum作为其unit参数:

public TUnitClass(string id, EQuantities quantity, Enum unit, string comment)
{
}

Json.NET将正确地反序列化代理TypeWrapper<TEnum>,但随后尝试将其强制转换为System.Enum以调用构造函数,结果失败,引发您看到的异常。

解决方案是添加一个接受代理TypeWrapper unit值的构造函数,并用[JsonConstructor]标记该构造函数。它可以是私有的,只要它标记有[JsonConstructor]。以下内容应该有效:

public class TUnitClass
{
[JsonConstructor]
TUnitClass(string id, EQuantities quantity, TypeWrapper<Enum> unit, string comment)
: this(id, quantity, unit.Value(), comment)
{
}
public TUnitClass(string id, EQuantities quantity, Enum unit, string comment)
{
Id = id;
Name = quantity.ToString();
Quantity = quantity;
Unit = unit;
Comment = comment;
FieldInfo fi = unit.GetType().GetField(unit.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
displayingString = attributes[0].Description;
}
public string Id { set; get; }
public string Name { set; get; }
public EQuantities Quantity { set; get; }
[JsonIgnore]
public System.Enum Unit { get; set; }
[JsonProperty("Unit", TypeNameHandling = TypeNameHandling.All)]
TypeWrapper<Enum> UnitSurrogate
{
get
{
return Unit == null ? null : TypeWrapper<Enum>.CreateWrapper(Unit);
}
set
{
Unit = value.Value();
}
}
public string Comment { set; get; }
public string displayingString { set; get; }
public TUnitClass Copy()
{
TUnitClass copiedUnitClass=new TUnitClass(Id,Quantity,Unit,Comment);
return copiedUnitClass;
}
}
public abstract class TypeWrapper<TBase>
{
protected TypeWrapper() { }
[JsonIgnore]
public abstract TBase BaseValue { get; }
public static TypeWrapper<TBase> CreateWrapper<TDerived>(TDerived value) where TDerived : TBase
{
if (value == null)
return null;
var type = value.GetType();
if (type == typeof(TDerived))
return new TypeWrapper<TDerived, TBase>(value);
// Return actual type of subclass
return (TypeWrapper<TBase>)Activator.CreateInstance(typeof(TypeWrapper<,>).MakeGenericType(type, typeof(TBase)), value);
}
}
public static class TypeWrapperExtensions
{
public static TBase Value<TBase>(this TypeWrapper<TBase> wrapper)
{
if (wrapper == null || wrapper.BaseValue == null)
return default(TBase);
return wrapper.BaseValue;
}
}
public sealed class TypeWrapper<TDerived, TBase> : TypeWrapper<TBase> where TDerived : TBase
{
public TypeWrapper() : base() { }
public TypeWrapper(TDerived value)
: base()
{
this.Value = value;
}
public override TBase BaseValue { get { return Value; } }
public TDerived Value { get; set; }
}

TypeNameAssemblyFormat = FormatterAssemblyStyle.Full应该不再是必需的。

注意,我修改了链接的答案以添加类型安全性,以确保序列化的类型实际上是System.Enum,这应该通过在反序列化过程中防止意外类型来提高安全性。有关TypeNameHandling安全性的一般性讨论,请参阅Newtonsoft Json中的TypeNameHandling警告。

更新

如果我有Enum[]而不是Enum,,我该如何处理此问题

您需要使用TypeWrapper<Enum>对象的代理数组,如下所示:

public class TUnitArrayClass
{
[JsonConstructor]
TUnitArrayClass(TypeWrapper<Enum>[] units)
: this(units == null ? null : units.Select(s => s.Value()).ToArray())
{
}
public TUnitArrayClass(IEnumerable<System.Enum> units)
{
this.Units = (units ?? Enumerable.Empty<System.Enum>()).ToArray();
}
[JsonIgnore]
public System.Enum[] Units { get; set; }
[JsonProperty("Unit", TypeNameHandling = TypeNameHandling.All)]
TypeWrapper<Enum> [] UnitsSurrogate
{
get
{
return Units == null ? null : Units.Select(u => TypeWrapper<Enum>.CreateWrapper(u)).ToArray();
}
set
{
if (value == null)
Units = null;
else
Units = value.Select(s => s.Value()).ToArray();
}
}
}

相关内容

  • 没有找到相关文章

最新更新