我正在尝试在C#中序列化/反序列化Dictionary<string, object>
。
对象可以是任何可序列化的东西。
Json.NET几乎可以工作,但如果字典中的值是enum
,则反序列化不正确,因为它被反序列化为long
。 TypeNameHandling.All
没有任何区别。
序列化库是否还有其他快速解决方案。结果不一定是JSON,但必须是文本。
我对传递给字典的数据也没有影响。我只需要序列化和反序列化我遇到的任何事情。
编辑:StringEnumConverter
没有帮助。数据被转换回Dictionary<string, object>
,因此解串器不知道序列化值是enum
。它将它视为一个对象,StringEnumConverter
在反序列化时仍为string
;它在没有转换器的情况下被反序列化为long
。 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<string, object>();
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<string, object> dict2
Assert.AreEqual(Foo.A, dict2["a"]);
Assert.AreEqual(Bar.B, dict2["b"]);
重要提示:我无法控制dict
;它实际上是一个派生自Dictionary<string, object>
的自定义类型:我只需要确保在反序列化时反序列化的所有键和值都来自同一类型,因此不需要进行转换。而且,我不必使用JSON;也许有一些其他的序列化器可以处理这项工作!?
答案 0 :(得分:3)
据推测,您已经使用TypeNameHandling.All
序列化字典,该字典应通过发送"$type"
对象属性以及对象本身来正确序列化和反序列化new Misc()
值。不幸的是,对于诸如枚举之类的类型(以及诸如int
和long
之类的其他类型),这不起作用,因为它们被序列化为JSON原语,没有机会包含{{1属性。
当序列化具有"$type"
值的字典时,解决方案是序列化可以封装类型信息的原始值的对象包装器,沿this answer行。由于您无法修改任何传入的对象,因此需要注入&#34;注入&#34;正确的包装器,您可以使用自定义合约解析程序将相应的item converter应用于字典值:
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>(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; }
}
示例fiddle。
最后,在使用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<Dictionary<string, object>>(json, settings);
时,请注意Newtonsoft docs中的这一注意事项:
当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。使用非None以外的值进行反序列化时,应使用自定义SerializationBinder验证传入类型。
有关可能需要执行此操作的讨论,请参阅 TypeNameHandling caution in Newtonsoft Json 。
答案 1 :(得分:0)
假设您有权修改对象类,可以将JsonCoverter属性添加到类的枚举成员中。
[JsonConverter(typeof(StringEnumConverter))]