使用Json.Net序列化NameValueCollection的自定义子类

我有以下类,我没有成功尝试序列化到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": []
    }
  ]
}

相关文章

AJAX是一种基于JavaScript和XML的技术,能够使网页实现异步交...
在网页开发中,我们常常需要通过Ajax从后端获取数据并在页面...
在前端开发中,经常需要循环JSON对象数组进行数据操作。使用...
AJAX(Asynchronous JavaScript and XML)是一种用于创建 We...
AJAX技术被广泛应用于现代Web开发,它可以在无需重新加载页面...
Ajax是一种通过JavaScript和HTTP请求交互的技术,可以实现无...