2008-10-01 5 views
13

Welche Optionen gibt es für die Serialisierung, wenn Instanzen von benutzerdefinierten Klassen von einem WebService zurückgegeben werden?Wie kann die benutzerdefinierte JSON-Serialisierung vom ASP.NET-Webdienst implementiert werden?

Wir haben einige Klassen mit einer Reihe von untergeordneten Auflistungsklasseneigenschaften sowie anderen Eigenschaften, die abhängig von der Verwendung festgelegt werden können oder nicht. Diese Objekte werden von einem ASP.NET .asmx WebService zurückgegeben, der mit dem ScriptService-Attribut versehen ist. Sie werden also über die JSON-Serialisierung serialisiert, wenn sie von den verschiedenen WebMethods zurückgegeben werden.

Das Problem ist, dass die Out-of-the-Box-Serialisierung alle öffentlichen Eigenschaften zurückgibt, unabhängig davon, ob sie verwendet werden oder nicht, und Klassenname und andere Informationen in einer ausführlicheren Weise zurückgeben, als gewünscht wäre Beschränken Sie die Menge des Verkehrs.

Derzeit für die Klassen zurückgegeben werden wir benutzerdefinierte Javascript-Wandler hinzugefügt, die die JSON serializtion handhaben, und fügte sie wie unten an der web.config:

<system.web.extensions> 
    <scripting> 
    <webServices> 
     <jsonSerialization> 
     <converters> 
      <add name="CustomClassConverter" type="Namespace.CustomClassConverter" /> 
     </converters> 
     </jsonSerialization> 
    </webServices> 
    </scripting> 
</system.web.extensions> 

Dies erfordert jedoch einen benutzerdefinierten Konverter für jede Klasse . Gibt es eine andere Möglichkeit, die Out of the Box-JSON-Serialisierung zu ändern, entweder durch Erweitern des Service, Erstellen eines benutzerdefinierten Serializers oder dergleichen?

Follow Up
@marxidad:

Wir sind mit dem DataContractJsonSerializer Klasse in anderen Anwendungen, aber ich habe es nicht gelungen, herauszufinden, wie es um diese Dienste anzuwenden. Hier ist ein Beispiel dafür, wie die Dienste Aufstellmaße:

[ScriptService] 
public class MyService : System.Web.Services.WebService 
{ 
    [WebMethod] 
    public CustomClass GetCustomClassMethod 
    { 
     return new customClass(); 
    } 
} 

webMethods durch JavaScript aufgerufen werden und gibt Daten in JSON serialisiert. Die einzige Methode, die wir in der Lage waren, die Serialisierung zu ändern, besteht darin, die oben genannten JavaScript-Konverter zu verwenden?

Gibt es eine Möglichkeit, dem WebService mitzuteilen, einen benutzerdefinierten DataContractJsonSerializer zu verwenden? Sei es durch web.config Konfiguration, dekorieren des Dienstes mit Attributen, etc.?

aktualisieren
Nun, wir finden konnten jede mögliche Weise, nicht wie oben die aus der Box JavaScriptSerializer außer für die Erstellung von individuellen JavaScriptConverters zu wechseln.

Was wir an diesem Ende getan haben, um zu verhindern, dass wir einen separaten Konverter erstellen mussten, war ein generischer JavaScriptConverter. Wir haben eine leere Schnittstelle zu den Klassen, die wir behandelt wollten und die SupportedTypes, die auf Web-Service-Start-up genannt wird verwendet Reflektion alle Typen zu finden, die die Schnittstelle der Art wie diese umzusetzen:

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 eigentliche Implementierung ist ein bisschen anders, so dass der Typ zwischengespeichert wird, und wir werden es wahrscheinlich umgestalten, um benutzerdefinierte Attribute anstelle einer leeren Schnittstelle zu verwenden.

Allerdings traten wir mit einem etwas anderen Problem beim Umgang mit benutzerdefinierten Sammlungen. Diese erweitern normalerweise nur eine generische Liste, aber die benutzerdefinierten Klassen werden anstelle der Liste <> selbst verwendet, da es in den Auflistungsklassen im Allgemeinen benutzerdefinierte Logik, Sortierung usw. gibt.

Das Problem ist, dass die Serialize-Methode für einen JavaScriptConverter ein Wörterbuch zurückgibt, das als Name-Wert-Paare mit dem zugeordneten Typ in JSON serialisiert wird, während eine Liste als ein Array zurückgegeben wird. Daher konnten die Sammlungsklassen nicht einfach mit dem Konverter serialisiert werden. Die Lösung hierfür bestand darin, diese Typen nicht einfach in die SupportedTypes des Konverters aufzunehmen und sie als Listen perfekt zu serialisieren.

So funktioniert Serialisierung, aber wenn Sie versuchen, diese Objekte als Parameter für einen Webdienstaufruf übergeben, die Deserialisierung bricht, da sie nicht die Eingabe als eine Liste von Zeichenfolge/Objekt behandelt werden kann Wörterbücher, die nicht in eine Liste der benutzerdefinierten Klasse der Sammlung konvertiert werden können. Der einzige Weg, den wir finden könnten, besteht darin, eine generische Klasse zu erstellen, die eine Liste von String/Objekt-Dictionaries darstellt, die die Liste in die entsprechende benutzerdefinierte Collection-Klasse konvertiert und dann alle Web-Service-Parameter so ändert, dass stattdessen die generische Klasse verwendet wird .

Ich bin sicher, es gibt Tonnen von Problemen und Verstöße gegen "Best Practices" hier, aber es macht den Job für uns erledigt, ohne eine Tonne von benutzerdefinierten Konverter-Klassen zu erstellen.

Antwort

0

Sie können die System.Runtime.Serialization.Json.DataContractJsonSerializer Klasse in der System.ServiceModel.Web.dll Baugruppe verwenden.

0

Zitieren Sie mich nicht auf diese Arbeit sicher, aber ich glaube, das ist, was Sie suchen.

[WebMethod] 
[ScriptMethod(ResponseFormat = ResponseFormat.Json)] 
public XmlDocument GetXmlDocument() 
{ 
    XmlDocument xmlDoc = new XmlDocument(); 
    xmlDoc.LoadXml(_xmlString); 
    return xmlDoc; 
} 
+1

Der WebService ist mit dem ScriptService-Attribut ausgestattet, so dass JSON standardmäßig zurückgegeben wird, sodass das ScriptMethod-Attribut nicht erforderlich ist. –

1

Wenn Sie .NET 3.x (oder can) verwenden, ist ein WCF-Dienst die beste Wahl.

Mit dem Attribut [DataMember] können Sie selektiv steuern, welche Eigenschaften serialisiert werden. WCF ermöglicht auch eine feinere Kontrolle über die JSON-Serialisierung und -Deserialisierung, wenn Sie dies wünschen.

Dies ist ein gutes Beispiel, um zu beginnen: http://blogs.msdn.com/kaevans/archive/2007/09/04/using-wcf-json-linq-and-ajax-passing-complex-types-to-wcf-services-with-json-encoding.aspx

+0

Es erfordert anonymen Zugriff auf den Dienst, der ein Hindernis sein kann. (Es war in meinem Fall von einer internen Windows Auth-basierten App.) –

+0

Wird nicht wirklich für uns arbeiten, obwohl wir 3.x verwenden können, sind wir vorläufig zumindest an die 2.0 Web Services gebunden. Sie sind ziemlich umfangreich und die Zeit für die Konvertierung ist ein Hindernis, also suchten wir nach einer schnellen Lösung, um sie leichter zu machen. Gute Verbindung obwohl. –

2

Wenn Sie nicht über Code-generierten Klassen verwenden, können Sie Ihre Objekte mit dem ScriptIgnoreAttribute schmücken den Serializer zu sagen, bestimmte Eigenschaften zu ignorieren. Die Xml-Serialisierung hat ein ähnliches Attribut.

Natürlich können Sie diesen Ansatz nicht verwenden, wenn Sie einige Eigenschaften einer Klasse für einen Service-Methodenaufruf und andere Eigenschaften derselben Klasse für einen anderen Service-Methodenaufruf zurückgeben möchten. Wenn Sie dies tun möchten, geben Sie in der Servicemethode einen anonymen Typ zurück. sowieso

[WebMethod] 
[ScriptMethod] 
public object GimmieData() 
{ 
    var dalEntity = dal.GimmieEntity(); //However yours works... 

    return new 
     { 
      id = dalEntity.Id, 
      description = dalEntity.Desc 
     }; 

} 

Der Serializer könnte weniger Pflege über die Art des Objekts, das Sie es senden, da sie es in dem Text gerade dreht.

Ich glaube auch, dass Sie ISerializable auf Ihre Datenentität implementieren können (als Teilklasse, wenn Sie Code-Gen-Dateneinheiten haben), um eine feinkörnige Kontrolle über den Serialisierungsprozess zu bekommen, aber ich habe es nicht versucht .

2

Ich weiß, dass dieser Thread für eine Weile ruhig war, aber ich dachte, ich würde anbieten, wenn Sie die SupportedTypes-Eigenschaft von JavaScriptConverter in Ihrem benutzerdefinierten Konverter überschreiben, können Sie die Typen hinzufügen, die den Konverter verwenden sollen. Dies könnte bei Bedarf in eine Konfigurationsdatei gehen. Auf diese Weise benötigen Sie keinen benutzerdefinierten Konverter für jede Klasse.

Ich habe versucht, einen generischen Konverter zu erstellen, konnte aber nicht herausfinden, wie man es in der web.config identifizieren kann. Ich würde gerne herausfinden, ob es noch jemand geschafft hat.

Ich kam auf die Idee, als ich versuchte, das obige Problem zu lösen und stolperte über Nick Berardis "Erstellen eines genaueren JSON .NET Serializers" (google it).

Arbeitete für mich :)

Danke an alle.

+0

das ist im Grunde, was wir tun ... Web-Konfiguration hat einen generischen Konverter und die SupportedTypes-Eigenschaft wird überschrieben. Die obigen Updates sind nicht der genaue Code, den wir verwenden, aber konzeptionell ist es dasselbe. –