NOTA: Sono entrato in molti dettagli su JavaScriptSerializer all'inizio della mia risposta, se si desidera solo leggere la risoluzione del problema noto di tipo menzionato nella domanda originale, saltare alla fine della risposta.
prestazioni
Basato sul benchmarks mi sono imbattuto, il JavaScriptSerializer è molto più lenta rispetto alle altre alternative e possono prendere 2 volte più a lungo per serializzare/deserializzare un oggetto rispetto al DataContractSerializer.
Non c'è bisogno di tipo noto
Detto questo, il JavaScriptSerializer è più flessibile in quanto non richiede di specificare 'tipi noti' prima del tempo, e il JSON serializzato è più pulita almeno in il caso dei dizionari (vedere esempi here).
L'altra faccia della flessibilità rispetto ai tipi noti è che non sarà possibile deserializzare quella stessa stringa JSON al tipo originale.Per esempio, supponiamo di avere una semplice Person
classe:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
E se creo un 'istanza di Dictinoary<string, object>
e aggiungere un'istanza della classe Person
ad esso prima di serializzazione:
var dictionary = new Dictionary<string, object>();
dictionary.Add("me", new Person { Name = "Yan", Age = 30 });
var serializer = new new JavaScriptSerializer();
var json = serializer .Serialize(dictionary);
io ottenere il seguente JSON {"me":{"Name":"Yan","Age":30}}
che è molto pulito ma privo di qualsiasi tipo di informazione. Quindi suppone se si dispone di due classi con le stesse definizioni membri o se Person
è una sottoclasse senza introdurre ulteriori membri:
public class Employee : Person
{
}
poi semplicemente non c'è alcun modo per il serializzatore di essere in grado di garantire che il JSON {"Name":"Yan","Age":30}
può essere deserializzato al tipo corretto.
Se si deserializzare {"me":{"Name":"Yan","Age":30}}
utilizzando il JavaScriptSerializer, nel dizionario si ottiene indietro il valore associato a "me" non è un'istanza di Person
ma un Dictionary<string, object>
invece, un semplice elenco di proprietà.
Se si desidera ottenere un'istanza Person
indietro, si potrebbe (anche se molto probabilmente non vorrebbe mai!) Conversione che Dictionary<string, object>
utilizzando il metodo ConvertToType
helper:
var clone = serializer.Deserialize<Dictionary<string, object>>(json);
var personClone = serializer.ConverToType<Person>(clone["me"]);
D'altra parte, se si non c'è bisogno di preoccuparsi di deserializzare quei JSON nel tipo corretto e la serailizzazione di JSON non è un collo di bottiglia nelle prestazioni (profila il tuo codice e scopri quanto tempo di CPU è speso per la serializzazione se non lo hai già fatto) quindi direi basta usare JavaScriptSerializer.
Iniezione di tipo noto
Se, alla fine della giornata, si ha ancora bisogno di utilizzare DataContractSerializer e la necessità di iniettare queste KnownTypes, qui ci sono due cose che si possono provare.
1) Passare la matrice di tipi noti a DataContractSerializer constructor.
2) Far passare una sottoclasse di DataContractResolver (con i mezzi per individuare i tipi di interesse a voi) per il DataContractSerializer constructor
È possibile creare un 'noto tipo di registro' di sorta che tiene traccia dei tipi che possono essere aggiunti al dizionario, e se è possibile controllare tutti i tipi che avrete bisogno di iniettare al DataContractSerializer, si può provare la cosa più semplice:
Creare una classe KnownTypeRegister
con metodi statici per aggiungere un tipo alla lista dei tipi noti:
public static class KnownTypeRegister
{
private static readonly ConcurrentBag _knownTypes = new ConcurrentBag();
public static void Add(Type type)
{
_knownTypes.Add(type);
}
public static IEnumerable Get()
{
return _knownTypes.ToArray();
}
}
Aggiungere un costruttore statico che registra i tipi con il registro:
[DataContract]
public class Person
{
static Person()
{
KnownTypeRegister.Add(typeof(Person));
}
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
}
Ottenere la matrice dei tipi conosciuti dal registro quando si costruisce il serializzatore:
var serializer = new DataContractSerializer(typeof(Dictionary<string, object>), KnownTypeRegister.Get());
Altre opzioni dinamiche/migliori sono possibili e ma sono anche più difficili da implementare, se vuoi saperne di più sulla risoluzione dei tipi noti dinamici, dai un'occhiata all'articolo MSDN di Juval Lowy sull'argomento here. Inoltre, this blog post di Carlos Figueira entra anche nei dettagli su tecniche più avanzate come generare dinamicamente i tipi, merita una lettura mentre sei sull'argomento!
Sono contento che alla gente piaccia già. Per favore non ti preoccupare di rispondere con argomenti come non è stato fatto per WCF ecc, ecc. Apprezzerei se puoi farci sapere se c'è un modo noto o se sei stato lì e rinunci per ragioni valide (cosa motivi?). – tishma