【问题标题】:Json.Net cannot serialize property of a class derived from dictionaryJson.Net 无法序列化从字典派生的类的属性
【发布时间】:2016-08-22 16:32:44
【问题描述】:

我有一个派生自 Dictionary 的类。类本身也有一个ClientId 属性。我想使用Json.Net将这个类的集合反序列化为JSON字符串所以遵循文档here

public interface IClientSettings: IDictionary<string, string>
{        
    string ClientId { get; set; }        
}

public class ClientSettings : Dictionary<string, string>, IClientSettings
{        
    public string ClientId { get; set; }
}

然后我将列表反序列化为字符串

        var list = new List<IClientSettings>();

        var client1 = new ClientSettings();
        client1.ClientId = "Client1";
        client1.Add("key1", "value1");
        client1.Add("key2", "value2");
        client1.Add("key3", "value3");

        var client2 = new ClientSettings();
        client1.ClientId = "Client2";
        client2.Add("key1", "value1");
        client2.Add("key2", "value2");
        client2.Add("key3", "value3");

        list.Add(client1);
        list.Add(client2);

        string json = JsonConvert.SerializeObject(list, Formatting.Indented);
        Console.WriteLine(json);

但是,这不会序列化 Clientid 属性。下面是输出。

[
  {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  },
  {
    "key1": "value1",
    "key2": "value2",
    "key3": "value3"
  }
]

我不确定我在这里缺少什么。我还发现了建议 here 建议进行自定义序列化,这是我唯一的选择吗?

【问题讨论】:

  • 您是否有理由从字典中派生而不是拥有字典属性?您可以通过一个具有 2 个属性的简单类轻松解决这个问题,字符串 ClientID 和 Dictionary 等等。它可以很好地序列化(太长,如上,字符串,字典)...输出 -[{"myDict":{"key1":"value1","key2":"value2","key3":" value3"},"ClientID":"Client1"},{"myDict":{"key1":"value1","key2":"value2","key3":"value3"},"ClientID":"Client2 "}]
  • 先前评论中提到的类。 [可序列化] public class ClientSettings { public string ClientID { get;放; } 公共字典 myDict; public ClientSettings() { myDict = new Dictionary(); } public void Add(string key, string value) { myDict.Add(key, value); } }

标签: serialization json.net


【解决方案1】:

是的,您是对的,您需要进行某种自定义序列化。如果使用[JsonObject] 标记类型,Json.NET 会将字典的键/值对序列化为 JSON 对象(使用 JsonDictionaryContract),或者将字典的属性序列化为 JSON 对象(使用 JsonObjectContract) - 但不是两者兼而有之。我怀疑两者都没有实现,以避免在字典包含与属性同名的键时发生运行时名称冲突的可能性,例如:

var client3 = new ClientSettings();
client3.ClientId = "Client1";
client3["ClientId"] = "Conflicting Value";

根据IETF standard

当对象中的名称不唯一时,接收此类对象的软件的行为是不可预测的。

因此,最好避免这种情况。

一种可能的实现方式如下:

[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
public class ClientSettings : Dictionary<string, string>, IClientSettings
{
    [JsonProperty]
    public string ClientId { get; set; }

    [JsonProperty]
    IDictionary<string, string> Items { get { return new DictionaryWrapper<string, string>(this); } }
}

使用

public class DictionaryWrapper<TKey, TValue> : IDictionary<TKey, TValue>
{
    readonly IDictionary<TKey, TValue> dictionary;

    public DictionaryWrapper(IDictionary<TKey, TValue> dictionary)
    {
        if (dictionary == null)
            throw new ArgumentNullException("dictionary");
        this.dictionary = dictionary;
    }

    #region IDictionary<TKey,TValue> Members

    public void Add(TKey key, TValue value) { dictionary.Add(key, value); }

    public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); }

    public ICollection<TKey> Keys { get { return dictionary.Keys; } }

    public bool Remove(TKey key) { return dictionary.Remove(key); }

    public bool TryGetValue(TKey key, out TValue value) { return dictionary.TryGetValue(key, out value); }

    public ICollection<TValue> Values { get { return dictionary.Values; } }

    public TValue this[TKey key]
    {
        get { return dictionary[key]; }
        set { dictionary[key] = value; }
    }

    #endregion

    #region ICollection<KeyValuePair<TKey,TValue>> Members

    public void Add(KeyValuePair<TKey, TValue> item) { dictionary.Add(item); }

    public void Clear() { dictionary.Clear(); }

    public bool Contains(KeyValuePair<TKey, TValue> item) { return dictionary.Contains(item); }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) { dictionary.CopyTo(array, arrayIndex); }

    public int Count { get { return dictionary.Count; } }

    public bool IsReadOnly { get { return dictionary.IsReadOnly; } }

    public bool Remove(KeyValuePair<TKey, TValue> item) { return dictionary.Remove(item); }

    #endregion

    #region IEnumerable<KeyValuePair<TKey,TValue>> Members

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return dictionary.GetEnumerator(); }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }

    #endregion
}

MemberSerialization.OptIn用于防止CountComparerKeysValues等基类属性被序列化。

这样,您的 JSON 将如下所示:

[
  {
    "ClientId": "Client2",
    "Items": {
      "key1": "value1",
      "key2": "value2",
      "key3": "value3"
    }
  }
]

【讨论】:

    猜你喜欢
    • 2014-03-31
    • 1970-01-01
    • 1970-01-01
    • 2015-12-27
    • 2017-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多