2013-08-22 24 views
7

如何讓Json.NET序列化程序將IDictionary<,>實例序列化爲具有鍵/值屬性的對象數組? 默認情況下,它將Key的值序列化爲JSON對象的屬性名稱。序列化字典<,>作爲Json.NET中的數組

基本上,我需要的東西是這樣的:的

[{"key":"some key","value":1},{"key":"another key","value":5}] 

代替:

{{"some key":1},{"another key":5}} 

我嘗試添加KeyValuePairConverter到串行設置,但它沒有任何效果。 (我發現這個轉換器被忽略類型IDictionary<>,但因爲它們是從其他圖書館收到我不能輕易改變我的對象的類型,所以改變從IDictionary<>ICollection<KeyValuePair<>>不是我的選擇。)

回答

5

我能讓這個轉換器工作。

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using Newtonsoft.Json; 
using Newtonsoft.Json.Converters; 

public class CustomDictionaryConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return (typeof(IDictionary).IsAssignableFrom(objectType) || 
       TypeImplementsGenericInterface(objectType, typeof(IDictionary<,>))); 
    } 

    private static bool TypeImplementsGenericInterface(Type concreteType, Type interfaceType) 
    { 
     return concreteType.GetInterfaces() 
       .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == interfaceType); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     Type type = value.GetType(); 
     IEnumerable keys = (IEnumerable)type.GetProperty("Keys").GetValue(value, null); 
     IEnumerable values = (IEnumerable)type.GetProperty("Values").GetValue(value, null); 
     IEnumerator valueEnumerator = values.GetEnumerator(); 

     writer.WriteStartArray(); 
     foreach (object key in keys) 
     { 
      valueEnumerator.MoveNext(); 

      writer.WriteStartObject(); 
      writer.WritePropertyName("key"); 
      writer.WriteValue(key); 
      writer.WritePropertyName("value"); 
      serializer.Serialize(writer, valueEnumerator.Current); 
      writer.WriteEndObject(); 
     } 
     writer.WriteEndArray(); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

下面是一個使用轉換器的例子:

IDictionary<string, int> dict = new Dictionary<string, int>(); 
dict.Add("some key", 1); 
dict.Add("another key", 5); 

string json = JsonConvert.SerializeObject(dict, new CustomDictionaryConverter()); 
Console.WriteLine(json); 

這裏是上面的輸出:

[{"key":"some key","value":1},{"key":"another key","value":5}] 
+1

它會更容易(和更完整),檢查非通用'System.Collections.IDictionary'代替。通用接口通常擴展非通用接口。另外,你的'TypeImplementsGenericInterface()'方法可以用Any()而不是'Where()。FirstOrDefault()!= null'來簡化。 –

+0

雖然它會很好,但IDictionary 實際上不會擴展非泛型的IDictionary接口。請參閱[文檔](http://msdn.microsoft.com/en-us/library/s4ys34ea.aspx)。但是,爲了完整起見,我編輯了我的答案,以添加對「IDictionary」的支持,並納入了關於簡化「TypeImplementsGenericInterface()'方法的其他建議。謝謝! –

+0

啊對了,我忘了那個。更正,_immutable_通用接口通常擴展相應的_immutable_非通用接口,不幸的是它不適用於此。 –

2

想通了另一種方式 - 你可以創建自定義ContractResolver和(de)序列化之前將其設置爲JsonSerializerSettings。下面的內容來源於內置的CamelCasePropertyNamesContractResolver,用於將序列化的屬性名稱轉換爲駝峯案例,但如果您不想修改名稱,則可以從DefaultContractResolver派生。

public class DictionaryFriendlyContractResolver : CamelCasePropertyNamesContractResolver 
{ 
    protected override JsonContract CreateContract(Type objectType) 
    { 
     if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) 
      return new JsonArrayContract(objectType); 
     if (objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))) 
      return new JsonArrayContract(objectType); 
     return base.CreateContract(objectType); 
    } 
} 

用法:

var cfg = new JsonSerializerSettings(); 
cfg.ContractResolver = new DictionaryFriendlyContractResolver(); 
string json = JsonConvert.SerializeObject(myModel, cfg); 
+0

儘管您的代碼適用於序列化,但它不能反序列化。這個答案適用於這兩種情況http://stackoverflow.com/a/25064637/2528649 – neleus

相關問題