• <bdo id='AbIbm'></bdo><ul id='AbIbm'></ul>
      <i id='AbIbm'><tr id='AbIbm'><dt id='AbIbm'><q id='AbIbm'><span id='AbIbm'><b id='AbIbm'><form id='AbIbm'><ins id='AbIbm'></ins><ul id='AbIbm'></ul><sub id='AbIbm'></sub></form><legend id='AbIbm'></legend><bdo id='AbIbm'><pre id='AbIbm'><center id='AbIbm'></center></pre></bdo></b><th id='AbIbm'></th></span></q></dt></tr></i><div id='AbIbm'><tfoot id='AbIbm'></tfoot><dl id='AbIbm'><fieldset id='AbIbm'></fieldset></dl></div>

      1. <legend id='AbIbm'><style id='AbIbm'><dir id='AbIbm'><q id='AbIbm'></q></dir></style></legend>
        <tfoot id='AbIbm'></tfoot>
      2. <small id='AbIbm'></small><noframes id='AbIbm'>

        使用 JSON.Net 对字典中的复杂类型进行特定用途的序列化

        时间:2023-05-23
          <tfoot id='gD6zp'></tfoot><legend id='gD6zp'><style id='gD6zp'><dir id='gD6zp'><q id='gD6zp'></q></dir></style></legend>

          1. <small id='gD6zp'></small><noframes id='gD6zp'>

              <tbody id='gD6zp'></tbody>

                <bdo id='gD6zp'></bdo><ul id='gD6zp'></ul>
                1. <i id='gD6zp'><tr id='gD6zp'><dt id='gD6zp'><q id='gD6zp'><span id='gD6zp'><b id='gD6zp'><form id='gD6zp'><ins id='gD6zp'></ins><ul id='gD6zp'></ul><sub id='gD6zp'></sub></form><legend id='gD6zp'></legend><bdo id='gD6zp'><pre id='gD6zp'><center id='gD6zp'></center></pre></bdo></b><th id='gD6zp'></th></span></q></dt></tr></i><div id='gD6zp'><tfoot id='gD6zp'></tfoot><dl id='gD6zp'><fieldset id='gD6zp'></fieldset></dl></div>

                2. 本文介绍了使用 JSON.Net 对字典中的复杂类型进行特定用途的序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

                  问题描述

                  限时送ChatGPT账号..

                  我有课

                  public class MyValue 
                  {
                      public String Prop1 { get; set; }
                      public String Prop2 { get; set; }
                  }
                  

                  我将其用作普通属性的类型以及字典键.

                  which I am both using as a type for a normal Property as well as a Dictionary key.

                  我需要的是一种方法,当这个类被用作属性时,它被序列化为

                  What I need is a way so that when this class is used as a Property, it is serialized as

                  {"Prop1":"foo","Prop2":"bar"}
                  

                  但是当它用作字典键时,它会以 JSON.Net 能够正确反序列化的方式进行序列化.

                  but when it is used as a Dictionary key, it is serialized in a way that JSON.Net is able to deserialize it properly.

                  向 MyValue 添加 ToString() 方法时,我能够创建一个文本表示(非 JSON),允许用作 Dictionary 键,但不幸的是,我之后无法反序列化它.即使为 MyValue 添加 JsonConverter 也无济于事,因为它似乎无法将非 JSON 作为源格式处理(此外,当作为属性序列化时,它是 json,因此转换器需要以某种方式处理两者).

                  When adding a ToString() method to MyValue, I am able to create a text representation (non-JSON) that allows the usage as Dictionary key but unfortunately, I am not able to deserialize it afterwards. Even adding a JsonConverter for MyValue did not help because it seems to be unable to handle non-JSON as source format (and additionally, when serialized as a property, it IS json, so the converter would need to handle both somehow).

                  推荐答案

                  你可以做的是在代理 KeyValuePair 数组中序列化和反序列化你的字典,像这样:

                  What you could do is serialize and deserialize your dictionary in a proxy KeyValuePair<string, string> array, like so:

                  [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")])

                  (Here I'm using the DataContract attributes, but I could just as easily have used [JsonIgnore] and [JsonProperty("Dictionary")])

                  因此,要对此进行测试(并假设您已正确覆盖 MyValue 上的 GetHashCode()Equals(),您需要为了将其用作字典键),我做了以下操作:

                  So, to test this (and assuming that you have properly overridden GetHashCode() and Equals() on MyValue, which you need to do in order to use it as a dictionary key), I did the following:

                  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:

                  Sure enough, there was no exception deserializing the container, but there was deserializing the dictionary directly. And the following JSON was created for the container:

                  {
                    "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 属性以获得相同的结果:

                  Or, if you don't like the proxy arrays, you could apply the following JsonConverterAttribute to each and every Dictionary property to get the same result:

                  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 并不完全符合您的要求需要.

                  As an alternative, you could seal your MyValue class and attach an appropriate TypeConverterAttribute for converting from & to a string. JSON.Net will pick this up and use it both for dictionary keys and properties. This solution is simpler in that it's a global solution so you don't need to use proxy arrays or converter properties for each and every dictionary, however the JSON created for your MyValue properties isn't quite what you require.

                  因此:

                  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 code = 0;
                              if (Prop1 != null)
                                  code ^= (uint)Prop1.GetHashCode();
                              code = (code << 16) | (code >> 16);
                              if (Prop2 != null)
                                  code ^= (uint)Prop2.GetHashCode();
                              return (int)code;
                          }
                      }
                  
                      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);
                      }
                  }
                  

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

                  Properties and dictionaries using this class can now be serialized without the use of any proxy arrays. For instance, serializing and deserializing the following:

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

                  序列化时提供以下 JSON:

                  Gives the following JSON when serialized:

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

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

                  (the quotes are escaped since they are embedded in the JSON, not part of the JSON.)

                  后期更新 - 为字典键创建通用 TypeConverter

                  可以使用适当的 TypeConverter.htm" rel="noreferrer">合约解析器:

                  It's possible to create a generic TypeConverter that works for any generically specified type by using an appropriate contract resolver:

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

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

                  Then apply it to your class as follows:

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

                  这篇关于使用 JSON.Net 对字典中的复杂类型进行特定用途的序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

                  上一篇:使用 Json.NET 对具有混合类型的数组进行序列化/反序列化 下一篇:c# JSON序列化使用值而不是属性名

                  相关文章

                    <legend id='MEe9W'><style id='MEe9W'><dir id='MEe9W'><q id='MEe9W'></q></dir></style></legend>
                    <i id='MEe9W'><tr id='MEe9W'><dt id='MEe9W'><q id='MEe9W'><span id='MEe9W'><b id='MEe9W'><form id='MEe9W'><ins id='MEe9W'></ins><ul id='MEe9W'></ul><sub id='MEe9W'></sub></form><legend id='MEe9W'></legend><bdo id='MEe9W'><pre id='MEe9W'><center id='MEe9W'></center></pre></bdo></b><th id='MEe9W'></th></span></q></dt></tr></i><div id='MEe9W'><tfoot id='MEe9W'></tfoot><dl id='MEe9W'><fieldset id='MEe9W'></fieldset></dl></div>
                    <tfoot id='MEe9W'></tfoot>

                      <small id='MEe9W'></small><noframes id='MEe9W'>

                      • <bdo id='MEe9W'></bdo><ul id='MEe9W'></ul>