使用C#中的枚举值反序列化Dictionary

我试图在C#中序列化/反序列化Dictionary 。 对象可以是可序列化的任何东西。

Json.NET几乎可以工作,但如果字典中的值是enum ,则反序列化不正确,因为它被反序列化为longTypeNameHandling.All没有任何区别。

是否还有其他快速的序列化库解决方案。 结果不一定是JSON,但必须是文本。

我对传递给字典的数据也没有影响。 我只需要序列化和反序列化我遇到的任何事情。

编辑: StringEnumConverter没有帮助。 数据被转换回Dictionary ,因此反序列化器不知道序列化值是enum 。 它将它视为一个对象,使用StringEnumConverter在反序列化时它仍然是一个string ; 如果没有转换器,它会被反序列化。 JSON.NET不保留枚举。

我想提供的解决方案是现有接口的实现,它被注入到我无法改变的现有解决方案中。

EDIT2:这是我想要做的一个例子

 public enum Foo { A, B, C } public enum Bar { A, B, C } public class Misc { public Foo Foo { get; set; } } var dict = new Dictionary(); dict.Add("a", Foo.A); dict.Add("b", Bar.B); dict.Add("c", new Misc()); // serialize dict to a string s // deserialize s to a Dictionary dict2 Assert.AreEqual(Foo.A, dict2["a"]); Assert.AreEqual(Bar.B, dict2["b"]); 

重要提示:我无法控制dict ; 它实际上是一个派生自Dictionary的自定义类型:我只需要确保在反序列化时反序列化的所有键和值都来自同一类型,因此不需要转换。 而且,我不必使用JSON; 也许有一些其他的序列化器可以处理这项工作!?

据推测,您已经使用TypeNameHandling.All序列化字典,它应该通过发出"$type"对象属性以及对象本身来正确地序列化和反序列化new Misc()值。 不幸的是,对于诸如枚举之类的类型(以及诸如intlong其他类型),这不起作用,因为它们被序列化为JSON原语,没有机会包含"$type"属性。

当使用object值序列化字典时,解决方案是按照此答案的行序列化可以封装类型信息的原始值的对象包装器。 由于您无法修改任何传入对象并需要“注入”正确的包装器,因此您可以使用自定义合约解析程序将相应的项目转换器应用于字典值:

 public class UntypedToTypedValueContractResolver : DefaultContractResolver { // As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons. // http://www.newtonsoft.com/json/help/html/ContractResolver.htm // http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm // "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance." // See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information static UntypedToTypedValueContractResolver instance; // Explicit static constructor to tell C# compiler not to mark type as beforefieldinit static UntypedToTypedValueContractResolver() { instance = new UntypedToTypedValueContractResolver(); } public static UntypedToTypedValueContractResolver Instance { get { return instance; } } protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) { var contract = base.CreateDictionaryContract(objectType); if (contract.DictionaryValueType == typeof(object) && contract.ItemConverter == null) { contract.ItemConverter = new UntypedToTypedValueConverter(); } return contract; } } class UntypedToTypedValueConverter : JsonConverter { public override bool CanConvert(Type objectType) { throw new NotImplementedException("This converter should only be applied directly via ItemConverterType, not added to JsonSerializer.Converters"); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var value = serializer.Deserialize(reader, objectType); if (value is TypeWrapper) { return ((TypeWrapper)value).ObjectValue; } return value; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (serializer.TypeNameHandling == TypeNameHandling.None) { Debug.WriteLine("ObjectItemConverter used when serializer.TypeNameHandling == TypeNameHandling.None"); serializer.Serialize(writer, value); } // Handle a couple of simple primitive cases where a type wrapper is not needed else if (value is string) { writer.WriteValue((string)value); } else if (value is bool) { writer.WriteValue((bool)value); } else { var contract = serializer.ContractResolver.ResolveContract(value.GetType()); if (contract is JsonPrimitiveContract) { var wrapper = TypeWrapper.CreateWrapper(value); serializer.Serialize(writer, wrapper, typeof(object)); } else { serializer.Serialize(writer, value); } } } } 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; } } 

然后使用它像:

 var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All, ContractResolver = UntypedToTypedValueContractResolver.Instance, Converters = new [] { new StringEnumConverter() }, // If you prefer }; var json = JsonConvert.SerializeObject(dict, Formatting.Indented, settings); var dict2 = JsonConvert.DeserializeObject>(json, settings); 

样品小提琴 。

最后,在使用TypeNameHandling ,请注意Newtonsoft文档中的这一注意事项:

当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。 使用非None以外的值进行反序列化时,应使用自定义SerializationBindervalidation传入类型。

有关可能需要这样做的讨论,请参阅Newtonsoft Json中的TypeNameHandling警告

假设您有权修改对象类,可以将JsonCoverter属性添加到类的枚举成员中。

 [JsonConverter(typeof(StringEnumConverter))]