web-dev-qa-db-fra.com

Comment ne pas sérialiser la propriété __type sur des objets JSON

Chaque objet que je renvoie d'une WebMethod d'une ScriptService est encapsulé dans un objet JSON avec les données d'une propriété nommée d. C'est bon. Mais je ne souhaite pas que la propriété supplémentaire __type soit transmise au client, car je traite manuellement avec jQuery.

C'est possible?

60
Robert

Eh bien ça fait longtemps que vous avez demandé. J'ai constaté que si je fais du constructeur par défaut de ma classe que ma méthode web retourne autre chose que public, elle ne sérialisera pas la partie __type: ClassName.

Vous voudrez peut-être déclarer votre constructeur par défaut protected internal ClassName(){} 

39
John Morrison

La solution de John n'a pas fonctionné pour moi car le type que je retourne est dans une DLL séparée. J'ai le contrôle total sur cette DLL mais je ne peux pas construire mon type de retour si le constructeur est interne.

Je me demandais si le type de retour étant un type public dans une bibliothèque pourrait même en être la cause - je faisais beaucoup d'Ajax et je n'avais jamais vu celui-ci auparavant. 

Tests rapides:

  • Temporairement déplacé la déclaration du type de retour dans App_Code. Toujours obtenir __type sérialisé.

  • Idem et appliqué le constructeur interne protégé par JM. Cela a fonctionné (donc il obtient un vote).

Étrangement, je ne reçois pas __type avec un type de retour générique:

[WebMethod]
public static WebMethodReturn<IEnumerable<FleetObserverLiteAddOns.VehicleAddOnAccountStatus>> GetAccountCredits()

La solution pour moi, cependant, était de laisser mon type de retour dans la balise DLL, mais de modifier le type de retour WebMethod en objet, c'est-à-dire.

[WebMethod]
public static object ApplyCredits(int addonid, int[] vehicleIds) 

au lieu de

[WebMethod]
public static WebMethodReturn ApplyCredits(int addonid, int[] vehicleIds)
23
Stephen Kennedy

J'ai essayé certaines de ces suggestions avec un service .NET 4 WCF et elles ne semblent pas fonctionner - la réponse JSON inclut toujours le type __.

Le moyen le plus simple que j'ai découvert pour supprimer l'indication de type consiste à modifier le comportement du noeud final de enableWebScript à webHttp.

    <behavior name="MapData.MapDataServiceAspNetAjaxBehavior">
      <webHttp />
    </behavior>

Le comportement enableWebScript par défaut est requis si vous utilisez un client ASP.NET AJAX, mais si vous manipulez le JSON avec JavaScript ou jQuery, le comportement webHttp est probablement un meilleur choix.

16
Jonathan Sayce

Si vous utilisez ServiceStack.Text JSON Serializer il vous suffit de:

JsConfig.ExcludeTypeInfo = true;

Cette fonctionnalité a été automatiquement ajoutée dans v2.28 , mais le code ci-dessus empêche cette opération de la sérialisation. Vous pouvez également modifier ce comportement de Type avec:

JsConfig<Type>.ExcludeTypeInfo = true;
10
Brett Veenstra

Passez à null pour JavaScriptTypeResolver et le type __ ne sera pas sérialisé

JavaScriptSerializer serializer = new JavaScriptSerializer(null);
string json = serializer.Serialize(foo);
3
Adam

Je pense avoir réduit la cause première du mystérieux "type"!

Voici un exemple où vous pouvez recréer le problème.

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class Test : System.Web.Services.WebService
{
    public class Cat
    {
        public String HairType { get; set; }
        public int MeowVolume { get; set; }
        public String Name { get; set; }
    }

    [WebMethod]
    public String MyMethodA(Cat cat)
    {
        return "return value does not matter";
    }

    [WebMethod]
    public Cat MyMethodB(String someParam)
    {
        return new Cat() { HairType = "Short", MeowVolume = 13, Name = "Felix the Cat" };
    }
}

Voici la partie clé!

Tout simplement parce que MyMethodA () existe dans ce même fichier .asmx et prend la classe Cat en tant que paramètre .... le type __ sera ajouté au JSON renvoyé par l'appel de l'autre méthode: MyMethodB ().

Même si ce sont des méthodes différentes !!

Ma théorie est la suivante: 

  1. Lorsque vous écrivez des services Web comme celui-ci, le code de Microsoft corrige automatiquement le comportement de sérialisation/désérialisation JSON pour vous puisque vous avez utilisé les attributs corrects, tels que [WebMethod] et [ScriptService].
  2. Lorsque ce code Microsoft auto-magique s'exécute, il trouve une méthode qui prend la classe Cat en tant que paramètre.
  3. Cela revient à dire ... oh ... ok ... eh bien, je vais recevoir un objet Cat de JSON ... donc ... si je jamais retourne un objet Cat au format JSON à partir de n’importe quelle méthode la classe de service Web en cours ... Je lui attribuerai une propriété __type afin qu'il soit facile de l'identifier ultérieurement lors de la désérialisation en C #.
  4. Nyah-hahahaha ...

Note importante à retenir

Vous pouvez éviter que la propriété __type apparaisse dans votre JSON généré en évitant de prendre la classe en question (Cat dans mon cas) en tant que paramètre de l'un de vos WebMethod dans votre service Web. Donc, dans le code ci-dessus, essayez simplement de modifier MyMethodA () pour supprimer le paramètre Cat. Cela entraîne la génération de la propriété __type not.

2
ClearCloud8

En plus des conseils de John Morrison sur internal ou protected internal constructeur dans votre classe DataContract, qui fonctionne très bien pour les services Web et la majorité de WCF, vous devrez peut-être apporter une modification supplémentaire à votre web.config file . Au lieu de <enableWebScript/>, utilisez <webHttp/> pour votre endpointBehaviors, par exemple:

<endpointBehaviors>
  <behavior name="MyServiceEndpoint">
    <webHttp/>
  </behavior>
</endpointBehaviors>
2
Art Kg

Je ne suis pas sûr que ce soit une bonne solution, mais si vous utilisez la bibliothèque Json.net , vous pouvez ignorer certaines propriétés en ajoutant l'attribut [JsonIgnore].

2
Mironline

Un peu tard pour le fil mais voilà.

Nous avons eu le même problème lorsque la propriété ajoutée à la chaîne json était une liste <T>. Ce que nous avons fait a été d'ajouter une autre propriété qui était un tableau de T, quelque chose comme.

Avant.

[DataMember]
public List<Person> People { get; set; }

Après.

public List<Person> People { get; set; }

[DataMember(Name = "People")]
public Person[] Persons {
    get {
        return People.ToArray();
    }
    private set { }
}

Bien que ce ne soit pas une solution idéale, cela fait l'affaire.

1
Michael

Cela devrait le résoudre.

Dans la méthode privée SerializeValue de JavaScriptSerializer dans System.WebExtensions.dll, Le type __ est ajouté à un dictionnaire interne s'il peut être résolu.

De réflecteur:

private void SerializeValue(object o, StringBuilder sb, int depth, Hashtable objectsInUse)
{
    if (++depth > this._recursionLimit)
    {
        throw new ArgumentException(AtlasWeb.JSON_DepthLimitExceeded);
    }
    JavaScriptConverter converter = null;
    if ((o != null) && this.ConverterExistsForType(o.GetType(), out converter))
    {
        IDictionary<string, object> dictionary = converter.Serialize(o, this);
        if (this.TypeResolver != null)
        {
            string str = this.TypeResolver.ResolveTypeId(o.GetType());
            if (str != null)
            {
                dictionary["__type"] = str;
            }
        }
        sb.Append(this.Serialize(dictionary));
    }
    else
    {
        this.SerializeValueInternal(o, sb, depth, objectsInUse);
    }
}

Si le type ne peut pas être déterminé, la sérialisation continuera, mais le type sera ignoré. La bonne nouvelle est que, puisque les types anonymes héritent de getType () et que les noms renvoyés sont générés dynamiquement par le compilateur, TypeResolver renvoie la valeur null pour ResolveTypeId et l'attribut "__type" est par la suite ignoré.

J'ai également suivi les conseils de John Morrison avec le constructeur interne juste au cas où, bien que j'utilisais uniquement cette méthode, je recevais toujours des propriétés __type dans ma réponse JSON.

//Given the following class
[XmlType("T")]
public class Foo
{
    internal Foo()
    {

    }

    [XmlAttribute("p")]
    public uint Bar
    {
        get;
        set;
    }
}

[WebService(Namespace = "http://me.com/10/8")]
[System.ComponentModel.ToolboxItem(false)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MyService : System.Web.Services.WebService
{

    //Return Anonymous Type to omit the __type property from JSON serialization
    [WebMethod(EnableSession = true)]
    [System.Web.Script.Services.ScriptMethod(UseHttpGet = false, ResponseFormat = ResponseFormat.Json, XmlSerializeString = false)]
    public object GetFoo(int pageId)
    {
        //Kludge, returning an anonymois type using link, prevents returning the _type attribute.
        List<Foo> foos = new List<Foo>();
        rtnFoos.Add( new Foo(){
            Bar=99
        }};

        var rtn = from g in foos.AsEnumerable()
                   select g;

        return rtn;
    }
}

Remarque: J'utilise un convertisseur de type JSON hérité qui lit les attributs de sérialisation XML à partir de types sérialisés pour compresser davantage le JSON. Merci à CodeJournal . Fonctionne comme un charme.

0
Laramie

N'utilisez pas l'attribut [Serializable]. 

Ce qui suit devrait juste le faire

JavaScriptSerializer ser = new JavaScriptSerializer (); chaîne json = ser.Serialize (objectClass);

0
sean

Voici un moyen de contourner ça

    [WebMethod]
    [ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)]
    public void Status()
    {
        MyObject myObject = new MyObject(); // Your class here
        var json = Newtonsoft.Json.JsonConvert.SerializeObject(myObject);

        HttpContext.Current.Response.Write(json);
    }
0
Liran BarNiv

Mes deux cents, bien que tard dans la journée: comme d'autres l'ont mentionné, il semble y avoir deux façons d'empêcher la propriété "__type": 

a) Protégez le constructeur sans paramètre 

b) Évitez de passer la classe en tant que paramètre à une méthode Web

Si vous n'avez jamais besoin de passer la classe en tant que paramètre, vous pouvez rendre le constructeur "protected internal". Si vous devez créer un objet vide, ajoutez une méthode fabrique ou un autre constructeur avec un paramètre factice.

Cependant, si vous devez passer la classe en tant que paramètre à une méthode Web, vous constaterez que cela ne fonctionnera pas si le constructeur sans paramètre est protégé (l'appel ajax échoue, vraisemblablement car les données passées en json ne peuvent pas être désérialisées dans votre classe ).

C'était mon problème, j'ai donc dû utiliser une combinaison de (a) et (b): protéger le constructeur sans paramètre et créer une classe dérivée fictive à utiliser exclusivement pour les paramètres de méthodes Web. Par exemple:

public class MyClass
{
    protected internal MyClass() { }
    public MyClass(Object someParameter) { }
    ...
}

// Use this class when we need to pass a JSON object into a web method
public class MyClassForParams : MyClass
{
    public MyClassForParams() : base() { }
}

Toute méthode Web devant utiliser MyClass utilise ensuite MyClassForParams:

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public MyClass DoSomething(MyClassForParams someObject)
{
    // Do something with someObject
    ...
    // Maybe return a MyClass object
    ...
}
0
Etherman

En plus de la réponse de @sean d'utiliser JavaScriptSerializer.

Lors de l'utilisation de JavaScriptSerializer et du marquage de la ResponseFormat = WebMessageFormat.Json de la méthode, la réponse résultante utilise un codage JSON double, ainsi que si la réponse résultante est string, elle sera exécutée entre guillemets doubles.

Pour éviter cela, utilisez la solution de cette excellente réponse pour définir le type de contenu en tant que JSON (écraser) et diffuser le résultat binaire de la variable JavaScriptSerializer.

L'échantillon de code de la réponse mentionnée:

public Stream GetCurrentCart()
{
    //Code ommited
    var j = new { Content = response.Content, Display=response.Display,
                  SubTotal=response.SubTotal};
    var s = new JavaScriptSerializer();
    string jsonClient = s.Serialize(j);
    WebOperationContext.Current.OutgoingResponse.ContentType =
        "application/json; charset=utf-8";
    return new MemoryStream(Encoding.UTF8.GetBytes(jsonClient));
}

JavaScriptSerializer est dans l'espace de noms System.Web.Script.Serialization trouvé dans System.Web.Extensions.dll qui n'est pas référencé par défaut.

0
Alex Pandrea