Welche Optionen gibt es für die Serialisierung, wenn Instanzen von benutzerdefinierten Klassen von einem WebService zurückgegeben werden?
Wir haben einige Klassen mit einer Reihe von Eigenschaften untergeordneter Sammlungsklassen sowie andere Eigenschaften, die je nach Verwendung festgelegt werden können oder nicht. Diese Objekte werden von einem ASP.NET .asmx WebService zurückgegeben, der mit dem ScriptService-Attribut dekoriert ist, und werden daher über JSON-Serialisierung serialisiert, wenn sie von den verschiedenen WebMethoden zurückgegeben werden.
Das Problem ist, dass die Standard-Serialisierung alle öffentlichen Eigenschaften zurückgibt, unabhängig davon, ob sie verwendet werden oder nicht, sowie den Klassennamen und andere Informationen in einer ausführlicheren Art und Weise zurückgibt, als es wünschenswert wäre, wenn Sie den Datenverkehr begrenzen wollen.
Derzeit haben wir für die zurückgegebenen Klassen benutzerdefinierte Javascript-Konverter hinzugefügt, die die JSON-Serialisierung handhaben, und sie der web.config wie folgt hinzugefügt:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization>
<converters>
<add name="CustomClassConverter" type="Namespace.CustomClassConverter" />
</converters>
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
Dies erfordert jedoch für jede Klasse einen eigenen Konverter. Gibt es einen anderen Weg, um die Out-of-the-Box-JSON-Serialisierung zu ändern, entweder durch die Erweiterung des Dienstes, die Erstellung eines benutzerdefinierten Serializer oder dergleichen?
Nachbereitung
@marxidad:
Wir verwenden die DataContractJsonSerializer-Klasse in anderen Anwendungen, aber ich habe nicht in der Lage gewesen, herauszufinden, wie man es auf diese Dienste anwenden. Hier ist ein Beispiel dafür, wie die Dienste eingerichtet sind:
[ScriptService]
public class MyService : System.Web.Services.WebService
{
[WebMethod]
public CustomClass GetCustomClassMethod
{
return new customClass();
}
}
Die WebMethods werden von Javascript aufgerufen und geben Daten zurück, die in JSON serialisiert sind. Die einzige Methode, die wir in der Lage waren, die Serialisierung zu ändern, ist die Verwendung der oben erwähnten Javascript-Konverter?
Gibt es eine Möglichkeit, den WebService zu sagen, einen benutzerdefinierten DataContractJsonSerializer zu verwenden? Sei es durch web.config Konfiguration, Dekoration des Dienstes mit Attributen, etc.
Update
Nun, wir konnten keine Möglichkeit finden, den standardmäßigen JavaScriptSerializer zu wechseln, außer durch die Erstellung individueller JavaScriptConverter wie oben.
Um zu verhindern, dass wir einen separaten Konverter erstellen müssen, haben wir einen generischen JavaScriptConverter erstellt. Wir fügten eine leere Schnittstelle zu den Klassen, die wir wollten behandelt und die SupportedTypes, die auf Web-Service-Start aufgerufen wird verwendet Reflexion, um alle Typen, die die Schnittstelle implementieren wie diese zu finden:
public override IEnumerable<Type> SupportedTypes
{
get
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AssemblyBuilder dynamicAssemblyCheck = assembly as AssemblyBuilder;
if (dynamicAssemblyCheck == null)
{
foreach (Type type in assembly.GetExportedTypes())
{
if (typeof(ICustomClass).IsAssignableFrom(type))
{
yield return type;
}
}
}
}
}
}
Die tatsächliche Implementierung ist ein wenig anders, so dass die Typen zwischengespeichert werden, und wir werden sie wahrscheinlich überarbeiten, um benutzerdefinierte Attribute anstelle einer leeren Schnittstelle zu verwenden.
Dabei stießen wir jedoch auf ein etwas anderes Problem beim Umgang mit benutzerdefinierten Sammlungen. Diese erweitern in der Regel nur eine generische Liste, aber die benutzerdefinierten Klassen werden anstelle der List<> selbst verwendet, weil es im Allgemeinen benutzerdefinierte Logik, Sortierung usw. in den Sammlungsklassen gibt.
Das Problem ist, dass die Serialize-Methode für einen JavaScriptConverter ein Wörterbuch zurückgibt, das als Name-Wert-Paare mit dem zugehörigen Typ in JSON serialisiert wird, während eine Liste als Array zurückgegeben wird. Die Auflistungsklassen konnten also nicht einfach mit dem Konverter serialisiert werden. Die Lösung für dieses Problem bestand darin, diese Typen nicht in die unterstützten Typen des Konverters aufzunehmen, so dass sie perfekt als Listen serialisiert werden.
Die Serialisierung funktioniert also, aber wenn man versucht, diese Objekte auf dem anderen Weg als Parameter für einen Webdienstaufruf zu übergeben, bricht die Deserialisierung ab, da sie nicht als Liste von String/Objekt-Wörterbüchern behandelt werden können, die nicht in eine Liste einer beliebigen benutzerdefinierten Klasse konvertiert werden können, die die Sammlung enthält. Der einzige Weg, den wir finden konnten, um damit umzugehen, ist eine generische Klasse zu erstellen, die eine Liste von String/Objekt-Wörterbüchern ist, die dann die Liste in die entsprechende benutzerdefinierte Auflistungsklasse konvertiert, und dann alle Webdienstparameter zu ändern, um stattdessen die generische Klasse zu verwenden.
Ich bin sicher, es gibt eine Menge Probleme und Verstöße gegen "Best Practices" hier, aber es wird die Arbeit für uns ohne die Schaffung einer Tonne von benutzerdefinierten Konverter-Klassen getan.