gpt4 book ai didi

c# - 在 C# 中使用枚举值反序列化 Dictionary

转载 作者:太空狗 更新时间:2023-10-30 01:32:05 24 4
gpt4 key购买 nike

我正在尝试序列化/反序列化 Dictionary<string, object>在 C# 中。对象可以是任何可序列化的对象。

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

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

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

编辑:StringEnumConverter没有帮助。数据被转换回 Dictionary<string, object> ,所以反序列化器不知道序列化值是 enum .它把它当作一个对象,用 StringEnumConverter它仍然是 string反序列化时;它被反序列化为 long没有转换器。 JSON.NET 不保留枚举。

我想提供的解决方案是现有接口(interface)的实现,该接口(interface)被注入(inject)到我无法更改的现有解决方案中。

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;也许还有其他一些序列化程序可以处理这项工作!?

最佳答案

大概你已经用 TypeNameHandling.All 序列化了你的字典,它应该通过发出 "$type" 正确序列化和反序列化 new Misc() 值对象属性以及对象本身。不幸的是,对于枚举类型(以及其他类型,例如 intlong),这不起作用,因为它们被序列化为 JSON 基元,没有机会包含"$type" 属性。

解决方案是,在使用object 值序列化字典时,按照this answer 的行序列化可以封装类型信息的原始值的对象包装器。 .由于您无法修改任何传入对象并且需要“注入(inject)”适当的包装器,因此您可以使用应用适当 item converter 的自定义契约(Contract)解析器来完成此操作。到字典值:

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

然后像这样使用它:

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

样本 fiddle .

最后,在使用 TypeNameHandling 时,请注意 Newtonsoft docs 中的警告:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

有关为什么这可能是必要的讨论,请参阅 TypeNameHandling caution in Newtonsoft Json .

关于c# - 在 C# 中使用枚举值反序列化 Dictionary<string, object>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38336390/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com