2011-06-20 22 views
7

Najwyraźniej IDictionary<string,object> jest serializowany jako tablica obiektów KeyValuePair (np. [{Key:"foo", Value:"bar"}, ...]). Czy istnieje możliwość serializacji jako obiektu (np. {foo:"bar"})?Serializowanie słowników z JavaScriptSerializer

+3

tak, nie używaj JavaScriptSerializer, jej całkowicie bzdura. Użyj Newtonsoft Json.NET –

Odpowiedz

10

Chociaż zgadzam się, że to bzdura JavaScriptSerializer i Json.Net jest lepszym rozwiązaniem , jest sposób, w jaki można przekształcić JavaScriptSerializer serializując tak, jak chcesz. Będziesz musiał zarejestrować konwertera i zastąpić metodę serializacji użyciu coś takiego:

public class KeyValuePairJsonConverter : JavaScriptConverter 
{ 
    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) 
    { 
     var instance = Activator.CreateInstance(type); 

     foreach (var p in instance.GetType().GetPublicProperties()) 
     { 
      instance.GetType().GetProperty(p.Name).SetValue(instance, dictionary[p.Name], null); 
      dictionary.Remove(p.Name); 
     } 

     foreach (var item in dictionary) 
      (instance).Add(item.Key, item.Value); 

     return instance; 
    } 
    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) 
    { 
     var result = new Dictionary<string, object>(); 
     var dictionary = obj as IDictionary<string, object>; 
     foreach (var item in dictionary) 
      result.Add(item.Key, item.Value); 
     return result; 
    } 
    public override IEnumerable<Type> SupportedTypes 
    { 
     get 
     { 
      return new ReadOnlyCollection<Type>(new Type[] { typeof(your_type) }); 
     } 
    } 
} 

JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); 
javaScriptSerializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJsonConverter() }); 
jsonOfTest = javaScriptSerializer.Serialize(test); 
// {"x":"xvalue","y":"\/Date(1314108923000)\/"} 

Nadzieja to pomaga!

+0

Próbuję użyć twojej klasy, ale wygląda na to, że twoja zmienna 'instance' z' Class Object' nie ma metody dodawania. –

4

Nie, nie jest to możliwe z JavaScriptSerializer. Jest to możliwe z Json.NET:

public class Bar 
{ 
    public Bar() 
    { 
     Foos = new Dictionary<string, string> 
     { 
      { "foo", "bar" } 
     }; 
    } 

    public Dictionary<string, string> Foos { get; set; } 
} 

, a następnie:

var bar = new Bar(); 
string json = JsonConvert.SerializeObject(bar, new KeyValuePairConverter()); 

przyniosłoby pożądany:

{"Foos":{"foo":"bar"}} 
+0

Najpierw jechałem tą trasą. Zobacz moje poprzednie pytanie: http://stackoverflow.com/questions/6416017/json-net-deserializing-nested-dictionaries. Miałem problem z deserializacją zagnieżdżonych słowników z Json.NET. – Daniel

+2

@ Daniel, Json.NET to cukier w porównaniu do 'JavaScriptSerializer'. Więc jeśli masz problemy z Json.NET, nie wiem, co powiedzieć na temat alternatywy :-) Jeśli chodzi o twoje inne pytanie, jest to całkowicie normalne zachowanie. Wszystko, co wskazałeś na serializer, to słownik łańcucha i obiektu. Pracujesz więc ze słabo wpisanymi słownikami w swoim kodzie, czego oczekujesz w zamian? Jedyne, co można uzyskać, to oczywiście JObjects o słabym typie. Użyj mocno napisanego słownika: 'Dictionary ', będzie różnica. –

+0

Na jaką różnicę się powołujesz? Nie widzę tego. – Daniel

1

Udało mi się go rozwiązać za pomocą JavaScriptSerializer, a sztuczka polega na stworzeniu własnego konwertera. Poniższy kod jest kod działa:

public class KeyValuePairJsonConverter : JavaScriptConverter { 
    public override object Deserialize(IDictionary<string, object> dictionary 
             , Type type 
             , JavaScriptSerializer serializer) { 
     throw new InvalidOperationException("Sorry, I do serializations only."); 
    } 

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { 
     Dictionary<string, object> result = new Dictionary<string, object>(); 
     Dictionary<string, MyClass> dictionaryInput = obj as Dictionary<string, MyClass>; 

     if (dictionaryInput == null) { 
      throw new InvalidOperationException("Object must be of Dictionary<string, MyClass> type."); 
     } 

     foreach (KeyValuePair<string, MyClass> pair in dictionaryInput) 
      result.Add(pair.Key, pair.Value); 

     return result; 
    } 

    public override IEnumerable<Type> SupportedTypes { 
     get { 
      return new ReadOnlyCollection<Type>(new Type[] { typeof(Dictionary<string, MyClass>) }); 
     } 
    } 
} 

A oto jak go używać:

JavaScriptSerializer js = new JavaScriptSerializer(); 
js.RegisterConverters(new JavaScriptConverter[] { new KeyValuePairJsonConverter() }); 
Context.Response.Clear(); 
Context.Response.ContentType = "application/json"; 
Context.Response.Write(js.Serialize(myObject)); 
1

Oto wierzę ulepszoną wersję z Tomasem odpowiedź. Działa jak marzenie. Moglibyśmy również dodać atrybut ScriptIgnore, ale dobrze, znokautujcie.

BTW, wybrałem JavaScriptSerializer, ponieważ moim zdaniem rozwiązania firm trzecich są w większości przypadków: mniej znane, długie instalowanie, często zapomniane żądania wstępne i rozmycie - prawo - które stwarzają ryzyko dla dystrybucji w biznesie.

P-S: Nie rozumiem, dlaczego próbowaliśmy deserializować zarówno do instancji, jak i do instancji jako słownik, więc usunąłem tę część.

public class KeyValuePairJsonConverter : JavaScriptConverter 
{ 
    public override object Deserialize(IDictionary<string, object> deserializedJSObjectDictionary, Type targetType, JavaScriptSerializer javaScriptSerializer) 
    { 
     Object targetTypeInstance = Activator.CreateInstance(targetType); 

     FieldInfo[] targetTypeFields = targetType.GetFields(BindingFlags.Public | BindingFlags.Instance); 

     foreach (FieldInfo fieldInfo in targetTypeFields) 
      fieldInfo.SetValue(targetTypeInstance, deserializedJSObjectDictionary[fieldInfo.Name]); 

     return targetTypeInstance; 
    } 

    public override IDictionary<string, object> Serialize(Object objectToSerialize, JavaScriptSerializer javaScriptSerializer) 
    { 
     IDictionary<string, object> serializedObjectDictionary = new Dictionary<string, object>(); 

     FieldInfo[] objectToSerializeTypeFields = objectToSerialize.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); 

     foreach (FieldInfo fieldInfo in objectToSerializeTypeFields) 
      serializedObjectDictionary.Add(fieldInfo.Name, fieldInfo.GetValue(objectToSerialize)); 

     return serializedObjectDictionary; 
    } 

    public override IEnumerable<Type> SupportedTypes 
    { 
     get 
     { 
      return new ReadOnlyCollection<Type>(new Type[] { typeof(YOURCLASSNAME) }); 
     } 
    } 
} 
+0

Odnośnie mojej uwagi PS. Być może miało to pozwolić na określenie typowego słownika, a nie typu, ale szczerze mówiąc nie widzę sensu określania ogólnego rodzaju słownika, ponieważ cała ta sprawa dotyczy deserializacji słowników o nazwie . I jest sześcienny. – Olograph

2

udało mi się rozwiązać z JavaScriptSerializer z LINQ Select:

var dictionary = new Dictionary<int, string>(); 
var jsonOutput = new JavaScriptSerializer().Serialize(dictionary.Select(x => new { Id = x.Key, DisplayText = x.Value })); 
+0

Nie działa to w złożonych przypadkach (to jest w złożonych strukturach hierarchicznych), ale z pewnością rozwiązuje problem w prostym przypadku, więc jest to rozwiązanie, które należy rozważyć, jeśli nie chcesz dodawać zestawu znaków do JavaScriptSerializer lub biblioteki innej firmy. –

+0

Dla mnie działało to nawet w skomplikowanych przypadkach, po prostu zmieniając na '.Serialize (słownik)'. Jest prostszy niż JsonConvert, gotowy do użycia. Chciałbym dać odpowiedź na tę odpowiedź –

Powiązane problemy