我有以下类,我没有成功尝试序列化到Json.
class HL7 : NameValueCollection { public List<HL7> Children { get; set; } public HL7() { Children = new List<HL7>(); } }
我已经像这样创建了对象并向其添加了数据:
HL7 hl7 = new HL7(); hl7.Add("a","123"); hl7.Add("b","456"); hl7.Children.Add(new HL7()); hl7.Children[0].Add("c","123"); hl7.Children[0].Add("d","456");
我打电话的时候
JsonConvert.SerializeObject(hl7)
我收到
["a","b"]
我期待以下:
{ "a": "123","b": "456","Children": [ { "c": "123","d": "456",} ] }
这里有一些事情:
> Json.NET无法在没有自定义转换器的情况下序列化NameValueCollection
,因为NameValueCollection实现了IEnumerable以迭代键,但没有实现IDictionary来迭代键和值.有关为何导致Json.NET出现问题的更全面解释,请参阅this answer.
>因为NameValueCollection实现了IEnumerable,Json.NET将您的类视为集合,因此将其序列化为JSON数组而不是具有命名属性的JSON对象.因此,您的孩子不是序列化的.同样,需要一个自定义转换器来解决这个问题.
>假设上述问题已解决,如果NameValueCollection的HL7子类碰巧有一个名为“Children”的键,则在序列化时会生成无效的JSON,即具有重复属性名称的对象.我建议移动名字和为了明确的序列化,将值转换为嵌套属性(命名为“Values”).
> NameValueCollection实际上可以为给定的键字符串提供多个字符串值,因此其条目值需要序列化为JSON数组而不是单个字符串.
将所有这些放在一起,以下代码:
[JsonConverter(typeof(HL7Converter))] public class HL7 : NameValueCollection { public List<HL7> Children { get; set; } public HL7() { Children = new List<HL7>(); } } public class HL7Converter : JsonConverter { class HL7Proxy { public NameValueCollectionDictionaryWrapper Values { get; set; } public List<HL7> Children { get; set; } } public override bool CanConvert(Type objectType) { return objectType == typeof(HL7); } public override object ReadJson(JsonReader reader,Type objectType,object existingValue,JsonSerializer serializer) { var proxy = serializer.Deserialize<HL7Proxy>(reader); if (proxy == null) return existingValue; var hl7 = existingValue as HL7; if (hl7 == null) hl7 = new HL7(); hl7.Add(proxy.Values.GetCollection()); if (proxy.Children != null) hl7.Children.AddRange(proxy.Children); return hl7; } public override void WriteJson(JsonWriter writer,object value,JsonSerializer serializer) { HL7 hl7 = (HL7)value; if (hl7 == null) return; serializer.Serialize(writer,new HL7Proxy { Children = hl7.Children,Values = new NameValueCollectionDictionaryWrapper(hl7) }); } } // Proxy dictionary to serialize & deserialize a NameValueCollection. We use a proxy dictionary rather than a real dictionary because NameValueCollection is an ordered collection but the generic dictionary class is unordered. public class NameValueCollectionDictionaryWrapper: IDictionary<string,string []> { readonly NameValueCollection collection; public NameValueCollectionDictionaryWrapper() : this(new NameValueCollection()) { } public NameValueCollectionDictionaryWrapper(NameValueCollection collection) { this.collection = collection; } // Method instead of a property to guarantee that nobody tries to serialize it. public NameValueCollection GetCollection() { return collection; } #region IDictionary<string,string[]> Members public void Add(string key,string[] value) { if (collection.GetValues(key) != null) throw new ArgumentException("Duplicate key " + key); foreach (var str in value) collection.Add(key,str); } public bool ContainsKey(string key) { return collection.GetValues(key) != null; } public ICollection<string> Keys { get { return collection.AllKeys; } } public bool Remove(string key) { bool found = ContainsKey(key); if (found) collection.Remove(key); return found; } public bool TryGetValue(string key,out string[] value) { value = collection.GetValues(key); return value != null; } public ICollection<string[]> Values { get { return Enumerable.Range(0,collection.Count).Select(i => collection.GetValues(i)).ToArray(); } } public string[] this[string key] { get { var value = collection.GetValues(key); if (value == null) throw new KeyNotFoundException(); return value; } set { Remove(key); Add(key,value); } } #endregion #region ICollection<KeyValuePair<string,string[]>> Members public void Add(KeyValuePair<string,string[]> item) { Add(item.Key,item.Value); } public void Clear() { collection.Clear(); } public bool Contains(KeyValuePair<string,string[]> item) { string [] value; if (!TryGetValue(item.Key,out value)) return false; return EqualityComparer<string[]>.Default.Equals(item.Value,value); // Consistent with Dictionary<TKey,TValue> } public void CopyTo(KeyValuePair<string,string[]>[] array,int arrayIndex) { foreach (var item in this) array[arrayIndex++] = item; } public int Count { get { return collection.Count; } } public bool IsReadOnly { get { return false; } } public bool Remove(KeyValuePair<string,string[]> item) { if (Contains(item)) return Remove(item.Key); return false; } #endregion #region IEnumerable<KeyValuePair<string,string[]>> Members public IEnumerator<KeyValuePair<string,string[]>> GetEnumerator() { foreach (string key in collection) { yield return new KeyValuePair<string,string[]>(key,collection.GetValues(key)); } } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion }
使用以下测试用例:
HL7 hl7 = new HL7(); hl7.Add("a","123"); hl7.Add("b","456"); hl7.Add("Children","Children"); hl7.Children.Add(new HL7()); hl7.Children[0].Add("c","123"); hl7.Children[0].Add("d","456"); hl7.Children[0].Add("d","789"); var json = JsonConvert.SerializeObject(hl7,Formatting.Indented); Debug.WriteLine(json);
提供以下JSON:
{ "Values": { "a": [ "123" ],"b": [ "456" ],"Children": [ "Children" ] },"Children": [ { "Values": { "c": [ "123" ],"d": [ "456","789" ] },"Children": [] } ] }