serialize serializar objeto manejo hacer generar estructura ejecutar desde c# asp.net json asmx javascriptserializer

serializar - manejo de json c#



Cómo no serializar la propiedad__type en objetos JSON (14)

¡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.

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?


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 <enableWebScript/> use <webHttp/> para sus <webHttp/> de punto final, por ejemplo:

<endpointBehaviors> <behavior name="MyServiceEndpoint"> <webHttp/> </behavior> </endpointBehaviors>


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


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<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 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 compilador, 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<Foo> foos = new List<Foo>(); 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.


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="")]


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.

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

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.


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 declaración 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<IEnumerable<FleetObserverLiteAddOns.VehicleAddOnAccountStatus>> 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)


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 ... }


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] .


No use el atributo [Serializable].

Lo siguiente debería hacerlo

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


Pase null para JavaScriptTypeResolver y el __type no se serializará

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


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


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<Type>.ExcludeTypeInfo = true;


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 <T>. Lo que hicimos fue agregar otra propiedad que era una matriz de T, algo así como.

Antes de.

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

Después.

public List<Person> 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.