2013-08-22 23 views
7

Json.NET serializer'ı IDictionary<,> örneğini anahtar/değer özelliklerine sahip nesneler dizisine serileştirmek için nasıl yapabilirim? Varsayılan olarak, anahtarın değerini JSON nesnesinin özellik adına serileştirir.Serialize Dictionary <,> Json.NET'te dizi olarak

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

yerine:

Temelde böyle bir şey ihtiyaç

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

Ben serileştirici ayarlarına KeyValuePairConverter eklemek çalıştı ama hiçbir etkisi yoktur. (Bu dönüştürücü IDictionary<> türü için yok sayılır ancak diğer kütüphanelerden alınır alınmaz kolayca benim nesnelerin türünü değiştiremezsiniz, bu nedenle IDictionary<> den ICollection<KeyValuePair<>> değişen benim için seçenek değil buldum.)

cevap

5

ben başardı bu dönüştürücüyü işe koy. İşte

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

dönüştürücü kullanılarak bir örnektir:

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

Ve burada yukarıdaki çıktısı:

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

yerine olmayan jenerik 'System.Collections.IDictionary' kontrol etmek daha kolay (ve daha tam) olacaktır. Genel arayüzler genellikle jenerik olmayan arayüzleri genişletir. Ayrıca, 'TypeImplementsGenericInterface()' yönteminiz 'Where() yerine' Any() 'kullanarak basitleştirilebilir. FirstOrDefault()! = Null'. –

+0

Güzel olsa da, "IDictionary " aslında jenerik olmayan 'IDictionary' arabirimini genişletmiyor. [Belgeler] 'e bakın (http://msdn.microsoft.com/en-us/library/s4ys34ea.aspx). Ancak, tamlık adına, 'IDictionary' için destek eklemek için cevabımı düzenledim ve 'TypeImplementsGenericInterface()' yöntemini basitleştirme ile ilgili diğer önerinizi ekledim. Teşekkürler! –

+0

Ah, bunu unuttum. Düzeltme, _immutable_ genel arayüzler genellikle ilgili _immutable_ non-generic arayüzü genişletir, maalesef burada geçerli değildir. Bunu yararlı bulabilen herkes için –

2

başka bir yol çözdü - özel ContractResolver oluşturabilir ve serileştirmeden önce (de) JsonSerializerSettings'a ayarlayın. Aşağıdaki özellik, serileştirilmiş özellik adlarını deve durumuna dönüştürmek için CamelCasePropertyNamesContractResolver'dan türetilmiştir, ancak adları değiştirmemeniz durumunda DefaultContractResolver türetilmiş olabilir.

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

Kullanımı:

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

Kodunuz serileştirme için çalışıyor olsa da, seri hale getirilemiyor. Bu cevap her iki durumda da çalışır. Http://stackoverflow.com/a/25064637/2528649 – neleus

İlgili konular