14 Stimmen

Wie implementiert man eine benutzerdefinierte JSON-Serialisierung von ASP.NET Web Service?

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.

2voto

ntcolonel Punkte 900

Wenn Sie keine Code-generierten Klassen verwenden, können Sie Ihre Eigenschaften mit dem ScriptIgnoreAttribute um dem Serialisierer mitzuteilen, dass er bestimmte Eigenschaften ignorieren soll. Die Xml-Serialisierung hat ein ähnliches Attribut.

Natürlich können Sie diesen Ansatz nicht verwenden, wenn Sie einige Eigenschaften einer Klasse bei einem Dienstmethodenaufruf und andere Eigenschaften derselben Klasse bei einem anderen Dienstmethodenaufruf zurückgeben möchten. Wenn Sie dies tun möchten, geben Sie in der Dienstmethode einen anonymen Typ zurück.

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

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

}

Der Serialisierer kümmert sich nicht um den Typ des Objekts, das Sie an ihn senden, da er es ohnehin nur in Text umwandelt.

Ich bin auch der Meinung, dass Sie Folgendes umsetzen könnten ISerializable auf Ihre Datenentität (als partielle Klasse, wenn Sie code-gen'd Datenentitäten haben), um feinkörnige Kontrolle über den Serialisierungsprozess zu erhalten, aber ich habe es nicht versucht.

2voto

Ich weiß, dieser Thread hat für eine Weile ruhig gewesen, aber ich dachte, ich würde anbieten, dass, wenn Sie die SupportedTypes Eigenschaft von JavaScriptConverter in Sie benutzerdefinierte Konverter überschreiben, können Sie die Typen, die den Konverter verwenden sollte hinzufügen. Dies könnte in eine Konfigurationsdatei gehen, wenn nötig. Auf diese Weise bräuchten Sie nicht für jede Klasse einen eigenen Konverter.

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

Ich kam auf die Idee, als ich versuchte, das obige Problem zu lösen und stolperte über Nick Berardi's "Creating a more accurate JSON .NET Serializer" (google it).

Hat bei mir funktioniert:)

Vielen Dank an alle.

1voto

Dave Ward Punkte 58382

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

Mit dem Attribut [DataMember] können Sie selektiv steuern, welche Eigenschaften an den Client 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 für den Anfang: 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

0voto

Mark Cidade Punkte 95914

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

0voto

Ty. Punkte 3768

Zitieren Sie mich nicht, wenn ich sicher bin, dass es funktioniert, aber ich glaube, das ist es, wonach Sie suchen.

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X