Cómo no serializar la propiedad __type en objetos JSON

Cada objeto que devuelvo de un WebMethod de un ScriptService está envuelto en un objeto JSON con los datos en una propiedad llamada d . Está bien. Pero no quiero que la propiedad __type adicional se sirva al cliente, ya que hago el procesamiento manual con jQuery.

¿Es posible?

Bueno, ha pasado mucho tiempo desde que preguntaste. Descubrí que si hago el constructor predeterminado de mi clase que mi método web devuelve algo que no sea público, no serializará la parte __type: ClassName.

Es posible que desee declarar su constructor predeterminado protected internal ClassName(){}

La solución de John no funcionó para mí, ya que el tipo que estoy devolviendo está en una DLL separada. Tengo control total sobre esa DLL pero no puedo construir mi tipo de devolución si el constructor es interno.

Me pregunté si el tipo de devolución sería un tipo público en una biblioteca, incluso podría ser la causa. He estado haciendo mucho Ajax y no he visto este antes.

Pruebas rápidas:

  • Movió temporalmente la statement del tipo de devolución a App_Code. Todavía obtener __type serializado.

  • Ídem y aplicado el constructor interno protegido por JM. Esto funcionó (por lo que recibe un voto).

Extrañamente, no obtengo __type con un tipo de devolución genérico:

 [WebMethod] public static WebMethodReturn> GetAccountCredits() 

La solución para mí, sin embargo, era dejar mi tipo de devolución en el archivo DLL pero cambiar el tipo de retorno WebMethod al objeto , es decir,

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

en lugar de

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

He estado probando algunas de estas sugerencias con un servicio .NET 4 WCF, y parece que no funcionan: la respuesta JSON todavía incluye __type.

La manera más fácil que he descubierto para eliminar la sugerencia de tipo es cambiar el comportamiento del punto final de enableWebScript a webHttp.

     

El comportamiento predeterminado de enableWebScript es obligatorio si está utilizando un cliente ASP.NET AJAX, pero si está manipulando el JSON con JavaScript o jQuery, entonces el comportamiento webHttp es probablemente una mejor opción.

Si está utilizando ServiceStack.Text JSON Serializer , solo necesita:

 JsConfig.ExcludeTypeInfo = true; 

Esta funcionalidad se agregó automáticamente en v2.28 , pero el código anterior lo mantiene fuera de la serialización. También puede cambiar este comportamiento por Type con:

 JsConfig.ExcludeTypeInfo = true; 

Pase null para JavaScriptTypeResolver y el __type no se serializará

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

No estoy seguro de que esta sea una buena solución, pero si usa la biblioteca Json.net , puede ignorar algunas propiedades agregando el atributo [JsonIgnore] .

¡Creo que he reducido la causa raíz del misterioso “tipo __type”!

Aquí hay un ejemplo donde puedes recrear el problema.

 [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" }; } } 

¡Aquí está la parte clave!

Simplemente porque MyMethodA () existe en este mismo archivo .asmx y toma la clase Cat como parámetro …. el __type se agregará al JSON devuelto al llamar al otro método: MyMethodB ().

¡Aunque son métodos diferentes!

Mi teoría es la siguiente:

  1. Al escribir servicios web como este, el código de Microsoft conecta automáticamente el comportamiento de serialización / deserialización de JSON por usted, ya que utilizó los atributos correctos, como [WebMethod] y [ScriptService].
  2. Cuando se ejecuta este código de magia automática de Microsoft, encuentra un método que toma en clase Cat como parámetro.
  3. Cifra … oh … bueno … bueno, ya que recibiré un objeto Cat de JSON … por lo tanto … si alguna vez devuelvo un objeto Cat como JSON de cualquier método en el servicio web actual clase … Le daré una propiedad de tipo __de modo que será fácil de identificar más tarde cuando deserialice de nuevo a C #.
  4. Nyah-jajajaja …

Nota importante para llevar

Puede evitar que la propiedad __type aparezca en su JSON generado al evitar tomar la clase en cuestión (Cat en mi caso) como un parámetro para cualquiera de sus WebMethods en su servicio web. Por lo tanto, en el código anterior, simplemente intente modificar MyMethodA () para eliminar el parámetro Cat. Esto hace que la propiedad __type no se genere.

Además del consejo de John Morrison sobre el constructor interno interno o protegido en su clase DataContract, que funciona sorprendentemente bien para los servicios web y la mayoría de WCF, es posible que deba realizar un cambio adicional en su archivo web.config. En lugar del elemento use para sus de punto final, por ejemplo:

      

Un poco tarde para el hilo, pero aquí va.

Tuvimos el mismo problema cuando la propiedad que se agregaba a la cadena json era una Lista . Lo que hicimos fue agregar otra propiedad que era una matriz de T, algo así como.

Antes de.

 [DataMember] public List People { get; set; } 

Después.

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

Si bien no es una solución ideal, hace el truco.

No use el atributo [Serializable].

Lo siguiente debería hacerlo

JavaScriptSerializer ser = new JavaScriptSerializer (); string json = ser.Serialize (objectClass);

Esto debería resolverlo.

En el método SerializeValue privado de JavaScriptSerializer en System.WebExtensions.dll, el __type se agrega a un diccionario interno si se puede resolver.

De Reflector:

 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 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 no se puede determinar el tipo, la serialización continuará, pero el tipo será ignorado. La buena noticia es que dado que los tipos anónimos heredan getType () y los nombres devueltos son generados dinámicamente por el comstackdor, TypeResolver devuelve null para ResolveTypeId y el atributo “__type” se ignora posteriormente.

También tomé el consejo de John Morrison con el constructor interno por si acaso, aunque usando solo este método, todavía obtenía propiedades __type en mi respuesta 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 foos = new List(); rtnFoos.Add( new Foo(){ Bar=99 }}; var rtn = from g in foos.AsEnumerable() select g; return rtn; } } 

Nota: Estoy usando un convertidor de tipo JSON heredado que lee los atributos de Serialización XML de tipos serializados para comprimir aún más el JSON. Gracias a CodeJournal . Funciona de maravilla.

Mis 2 centavos, sin embargo tarde en el día: como otros han mencionado, parece haber dos formas de evitar la propiedad “__type”:

a) Proteger el constructor sin parámetros

b) Evita pasar la clase como un parámetro para un método web

Si nunca necesita pasar la clase como parámetro, puede hacer que el constructor sea “interno protegido”. Si necesita crear un objeto vacío, agregue un método de fábrica o algún otro constructor con un parámetro ficticio.

Sin embargo, si necesita pasar la clase como un parámetro a un método web, entonces encontrará que esto no funcionará si el constructor sin parámetros está protegido (la llamada ajax falla, presumiblemente porque los datos de json pasados ​​no se pueden deserializar en su clase )

Este era mi problema, así que tuve que usar una combinación de (a) y (b): proteger el constructor sin parámetros y crear una clase derivada ficticia para usar exclusivamente para los parámetros de los métodos web. P.ej:

 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() { } } 

Cualquier método web que necesite tomar en MyClass utiliza en su lugar MyClassForParams:

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

Resolví declarar el constructor predeterminado protegido ClassName interno () {} Ver también la respuesta de John Morrison

Esto es un truco, pero esto funcionó para mí (usando C #):

 s = (JSON string with "__type":"clsname", attributes) string match = "\"__type\":\"([^\\\"]|\\.)*\","; RegEx regex = new Regex(match, RegexOptions.Singleline); string cleaned = regex.Replace(s, ""); 

Funciona con [DataContract] y [DataContract(Namespace="")]