使用JSON.Net的字典中复杂类型的特定于用法的序列化

使用JSON.Net的字典中复杂类型的特定于用法的序列化,第1张

使用JSON.Net的字典中复杂类型的特定于用法的序列化

您可以做的是在代理

KeyValuePair<string, string>
数组中序列化和反序列化字典,如下所示:

[DataContract]public class MyContainer{    public MyContainer() {        this.Dictionary = new Dictionary<MyValue, int>();    }    [DataMember]    public MyValue MyValue { get; set; }    [IgnoreDataMember]    public Dictionary<MyValue, int> Dictionary { get; set; }    [DataMember(Name="Dictionary")]    private KeyValuePair<MyValue, int> [] SerializedDictionary    {        get        { if (Dictionary == null)     return null; return Dictionary.ToArray();        }        set        { if (value == null) {     Dictionary = null; } else {     Dictionary = value.ToDictionary(pair => pair.Key, pair => pair.Value); }        }    }}

(在这里,我正在使用

DataContract
属性,但是我可以很容易地使用
[JsonIgnore]
[JsonProperty("Dictionary")]

因此,要对此进行测试(并假设您已正确覆盖

GetHashCode()
并且
Equals()
在上进行了重写
MyValue
,然后才能将其用作字典键),我做了以下工作:

public static class TestDictionaryJson{    public static void Test()    {        var dict = new Dictionary<MyValue, int>();        dict[(new MyValue("A", "A"))] = 1;        dict[(new MyValue("B", "B"))] = 2;        var myContainer = new MyContainer() { MyValue = new MyValue("A Property", "At the top level"), Dictionary = dict };        var json = JsonConvert.SerializeObject(myContainer, Formatting.Indented);        Debug.WriteLine(json);        try        { var newContainer = JsonConvert.DeserializeObject<MyContainer>(json);        }        catch (Exception ex)        { Debug.Assert(false, ex.ToString()); // No assert - no exception is thrown.        }        try        { var dictjson = JsonConvert.SerializeObject(dict, Formatting.Indented); Debug.WriteLine(dictjson); var newDict = JsonConvert.DeserializeObject<Dictionary<MyValue, int>>(dictjson);        }        catch (Exception ex)        { Debug.WriteLine("Caught expected exception deserializing dictionary directly: " + ex.ToString());        }    }}

当然,对容器反序列化也没有例外,但是直接对字典 进行 反序列化。并且为容器创建了以下JSON:

{  "MyValue": {    "Prop1": "A Property",    "Prop2": "At the top level"  },  "Dictionary": [    {      "Key": {        "Prop1": "A",        "Prop2": "A"      },      "Value": 1    },    {      "Key": {        "Prop1": "B",        "Prop2": "B"      },      "Value": 2    }  ]}

那是你要的吗?

更新资料

或者,如果您不喜欢代理数组,则可以将以下内容

JsonConverterAttribute
应用于每个
Dictionary
属性,以获得相同的结果:

public class MyContainer{    public MyContainer()    {        this.Dictionary = new Dictionary<MyValue, int>();    }    public MyValue MyValue { get; set; }    [JsonConverter(typeof(DictionaryToArrayConverter<MyValue, int>))]    public Dictionary<MyValue, int> Dictionary { get; set; }}public class DictionaryToArrayConverter<TKey, TValue> : JsonConverter{    public override bool CanConvert(Type objectType)    {        return objectType == typeof(Dictionary<TKey, TValue>);    }    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)    {        KeyValuePair<TKey, TValue>[] pairs;        JToken token = JToken.Load(reader);        if (token.Type == JTokenType.Array)        { pairs = token.ToObject<KeyValuePair<TKey, TValue>[]>(serializer);        }        else        { JArray array = new JArray(); array.Add(token); pairs = token.ToObject<KeyValuePair<TKey, TValue>[]>(serializer);        }        if (pairs == null) return null;        return pairs.ToDictionary(pair => pair.Key, pair => pair.Value);    }    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)    {        if (value == null) return;        var pairs = ((IDictionary<TKey, TValue>)value).ToArray();        serializer.Serialize(writer, pairs);    }}

更新资料

作为替代方案,您可以密封您的

MyValue
课程,并附加一个适当的内容,
TypeConverterAttribute
用于将&转换为字符串。JSON.Net会选择并将其用于字典键和属性。该解决方案比较简单,因为它是一个全局解决方案,因此您不需要为每个字典都使用代理数组或转换器属性,但是为您的
MyValue
属性创建的JSON
并不是您所需要的。

从而:

public class MyValueConverter : TypeConverter{    public override bool CanConvertFrom(ITypeDescriptorContext context,       Type sourceType)    {        if (sourceType == typeof(string))        { return true;        }        return base.CanConvertFrom(context, sourceType);    }    public override object ConvertFrom(ITypeDescriptorContext context,       CultureInfo culture, object value)    {        if (value is string)        { // Cannot do JsonConvert.DeserializeObject here because it will cause a stackoverflow exception. using (var reader = new JsonTextReader(new StringReader((string)value))) {     JObject item = JObject.Load(reader);     if (item == null)         return null;     MyValue myValue = new MyValue();     var prop1 = item["Prop1"];     if (prop1 != null)         myValue.Prop1 = prop1.ToString();     var prop2 = item["Prop2"];     if (prop2 != null)         myValue.Prop2 = prop2.ToString();     return myValue; }        }        return base.ConvertFrom(context, culture, value);    }    public override object ConvertTo(ITypeDescriptorContext context,       CultureInfo culture, object value, Type destinationType)    {        if (destinationType == typeof(string))        { MyValue myValue = (MyValue)value; // Cannot do JsonConvert.SerializeObject here because it will cause a stackoverflow exception. StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture)) using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) {     jsonWriter.WriteStartObject();     jsonWriter.WritePropertyName("Prop1");     jsonWriter.Writevalue(myValue.Prop1);     jsonWriter.WritePropertyName("Prop2");     jsonWriter.Writevalue(myValue.Prop2);     jsonWriter.WriteEndObject();     return sw.ToString(); }        }        return base.ConvertTo(context, culture, value, destinationType);    }}[TypeConverter(typeof(MyValueConverter))]public class MyValue{    public MyValue()    {    }    public MyValue(string prop1, string prop2)    {        this.Prop1 = prop1;        this.Prop2 = prop2;    }    public String Prop1 { get; set; }    public String Prop2 { get; set; }    public override bool Equals(object obj)    {        if (ReferenceEquals(this, obj)) return true;        else if (ReferenceEquals(obj, null)) return false;        if (GetType() != obj.GetType()) return false;        var other = (MyValue)obj;        return Prop1 == other.Prop1 && Prop2 == other.Prop2;    }    public override int GetHashCode()    {        unchecked        { uint pre = 0; if (Prop1 != null)     pre ^= (uint)Prop1.GetHashCode(); pre = (pre << 16) | (pre >> 16); if (Prop2 != null)     pre ^= (uint)Prop2.GetHashCode(); return (int)pre;        }    }    public override string ToString()    {        return TypeDescriptor.GetConverter(GetType()).ConvertToString(this);    }    public static bool operator ==(MyValue first, MyValue second)    {        if (ReferenceEquals(first, null)) return ReferenceEquals(second, null);        return first.Equals(second);    }    public static bool operator !=(MyValue first, MyValue second)    {        return !(first == second);    }}

现在可以在不使用任何代理数组的情况下序列化使用此类的属性和词典。例如,序列化和反序列化以下内容:

public class MyContainer{    public MyContainer()    {        this.Dictionary = new Dictionary<MyValue, int>();    }    public MyValue MyValue { get; set; }    public Dictionary<MyValue, int> Dictionary { get; set; }}

序列化时提供以下JSON:

{  "MyValue": "{"Prop1":"A Property","Prop2":"At the top level"}",  "Dictionary": {    "{"Prop1":"A","Prop2":"A"}": 1,    "{"Prop1":"B","Prop2":"B"}": 2  }}

(引号被转义,因为它们嵌入在JSON中,而不是JSON的一部分。)

后期更新-

TypeConverter
为字典键创建通用

TypeConverter
通过使用适当的合同解析器,可以创建适用于任何通用指定类型的通用:

public class NoTypeConverterContractResolver : DefaultContractResolver{    readonly Type type;    public NoTypeConverterContractResolver(Type type)        : base()    {        if (type == null) throw new ArgumentNullException();        if (type == typeof(string) || type.IsPrimitive) throw new ArgumentException("type == typeof(string) || type.IsPrimitive");        this.type = type;    }    protected override JsonContract CreateContract(Type objectType)    {        if (type.IsAssignableFrom(objectType))        { // Replaces JsonStringContract for the specified type. var contract = this.CreateObjectContract(objectType); return contract;        }        return base.CreateContract(objectType);    }}public class GenericJsonTypeConverter<T> : TypeConverter{    // 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."    static NoTypeConverterContractResolver contractResolver;    static NoTypeConverterContractResolver ContractResolver    {        get        { if (contractResolver == null)     Interlocked.CompareExchange(ref contractResolver, new NoTypeConverterContractResolver(typeof(T)), null); return contractResolver;        }    }    public override bool CanConvertFrom(ITypeDescriptorContext context,       Type sourceType)    {        if (sourceType == typeof(string))        { return true;        }        return base.CanConvertFrom(context, sourceType);    }    public override object ConvertFrom(ITypeDescriptorContext context,       CultureInfo culture, object value)    {        if (value is string)        { using (var reader = new JsonTextReader(new StringReader((string)value))) {     var obj = JsonSerializer.Create(new JsonSerializerSettings { ContractResolver = ContractResolver }).Deserialize<T>(reader);     return obj; }        }        return base.ConvertFrom(context, culture, value);    }    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)    {        if (destinationType == typeof(string))        { StringBuilder sb = new StringBuilder(); using (StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture)) using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) {     JsonSerializer.Create(new JsonSerializerSettings { ContractResolver = ContractResolver }).Serialize(jsonWriter, value); } return sb.ToString();        }        return base.ConvertTo(context, culture, value, destinationType);    }}

然后将其应用于您的班级,如下所示:

[TypeConverter(typeof(GenericJsonTypeConverter<MyValue>))]public class MyValue{}


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5642601.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存