2008-10-01 12 views
13

¿Qué opciones hay para la serialización al devolver instancias de clases personalizadas desde un servicio web?¿Cómo implementar la serialización JSON personalizada desde el servicio web ASP.NET?

Tenemos algunas clases con varias propiedades de clase de colección secundaria, así como otras propiedades que pueden configurarse o no según el uso. Estos objetos se devuelven desde un ASP.NET .asmx WebService decorado con el atributo ScriptService, por lo que se serializan a través de la serialización JSON cuando los devuelven varios WebMethods.

El problema es que la serialización lista para usar devuelve todas las propiedades públicas, independientemente de si se usan o no, así como el nombre de la clase y otra información de una manera más detallada de lo que se desearía si quisiera limitar la cantidad de tráfico.

Actualmente, para ser devueltos a las clases que hemos añadido convertidores JavaScript personalizadas que manejan el serializtion JSON, e incluirlos en el web.config de la siguiente manera:

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

Pero esto requiere un convertidor de medida para cada clase . ¿Hay alguna otra forma de cambiar la serialización JSON de fábrica, ya sea extendiendo el servicio, creando un serializador personalizado o similar?

Seguimiento
@marxidad:

Estamos utilizando la clase DataContractJsonSerializer en otras aplicaciones, sin embargo, han sido incapaces de encontrar la manera de aplicarla a estos servicios. Aquí está un ejemplo de cómo son los servicios de set-up:

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

webMethods son llamados por javascript y datos serializados en JSON retorno. ¿El único método que hemos podido cambiar la serialización es usar los convertidores de JavaScript como se mencionó anteriormente?

¿Hay alguna manera de decirle al WebService que use un DataContractJsonSerializer personalizado? Ya sea por la configuración web.config, decorando el servicio con atributos, etc.

actualización
Bueno, no pudimos encontrar ninguna manera de cambiar la de la caja JavaScriptSerializer a excepción de la creación de JavaScriptConverters individuales que el anterior.

Lo que hicimos con ese fin para evitar tener que crear un convertidor separado fue crear un JavaScriptConverter genérico. Hemos añadido una interfaz vacía a las clases que queríamos ocupó y los SupportedTypes que se llama el servicio web puesta en marcha utiliza la reflexión para encontrar tipos que implementan el tipo de interfaz de la siguiente manera:

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

La implementación real es un poco diferente para que el tipo esté en caché, y probablemente lo refaccionaremos para usar atributos personalizados en lugar de una interfaz vacía.

Sin embargo, con esto, nos encontramos con un problema ligeramente diferente cuando se trata de colecciones personalizadas. Por lo general, estos solo extienden una lista genérica, pero las clases personalizadas se usan en lugar de la Lista <> misma porque generalmente hay una lógica personalizada, clasificación, etc. en las clases de colección.

El problema es que el método Serialize para un JavaScriptConverter devuelve un diccionario que se serializa en JSON como pares de nombre y valor con el tipo asociado, mientras que una lista se devuelve como una matriz. Por lo tanto, las clases de recopilación no se podían serializar fácilmente con el convertidor. La solución para esto fue simplemente no incluir esos tipos en SupportedTypes del convertidor y se serializaron perfectamente como listas.

Entonces, la serialización funciona, pero cuando intenta pasar estos objetos de otra manera como un parámetro para una llamada de servicio web, la deserialización se rompe, porque no pueden ser la entrada se trata como una lista de cadena/objeto diccionarios, que no se pueden convertir a una lista de cualquier clase personalizada que contenga la colección. La única forma que podemos encontrar para resolver esto es crear una clase genérica que sea una lista de diccionarios de cadenas/objetos que luego convierta la lista a la clase de recopilación personalizada adecuada y luego cambiar los parámetros del servicio web para usar la clase genérica. .

Estoy seguro de que hay un montón de problemas y violaciones de las "mejores prácticas" aquí, pero nos hace el trabajo sin crear un montón de clases de conversión personalizadas.

Respuesta

0

No me digas que esto funciona con seguridad, pero creo que esto es lo que estás buscando.

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

El servicio web está decorado con el atributo ScriptService, por lo que devuelve JSON de forma predeterminada, por lo que el atributo ScriptMethod no es necesario. –

1

Si está utilizando .NET 3.x (o puede), un servicio de WCF será su mejor opción.

Puede controlar selectivamente qué propiedades se serializan al cliente con el atributo [DataMember]. WCF también permite un control más detallado sobre la serialización JSON y la deserialización, si lo desea.

Este es un buen ejemplo para empezar: 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

Requiere acceso anónimo al servicio que puede ser un obstáculo. (Ha sido en mi caso una aplicación interna basada en autenticación de Windows.) –

+0

En realidad, no funcionará para nosotros, aunque podemos usar 3.x, por el momento estamos vinculados al menos a los servicios web 2.0. Son bastante extensos y el tiempo de conversión es un obstáculo, por lo que estábamos buscando una solución rápida para hacerlos más livianos. Buen enlace sin embargo. –

2

Si usted no utiliza clases generada por código, se puede decorar sus propiedades con el ScriptIgnoreAttribute para decirle al serializador hacer caso omiso de ciertas propiedades. La serialización Xml tiene un atributo similar.

Por supuesto, no puede usar este enfoque si desea devolver algunas propiedades de una clase en una llamada de método de servicio y diferentes propiedades de la misma clase en una llamada a método de servicio diferente. Si desea hacer eso, devuelva un tipo anónimo en el método de servicio.

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

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

} 

El serializador le importa nada el tipo de objeto que envíe a la misma, ya que simplemente lo convierte en texto de todos modos.

También creo que podría implementar ISerializable en su entidad de datos (como una clase parcial si tiene entidades de datos genéricos) para obtener un control detallado sobre el proceso de serialización, pero no lo he intentado .

2

Sé que este hilo ha estado en silencio por un tiempo, pero pensé que ofrecería que si anula la propiedad SupportedTypes de JavaScriptConverter en su convertidor personalizado, puede agregar los tipos que deberían usar el convertidor. Esto podría entrar en un archivo de configuración si es necesario. De esta forma, no necesitarías un convertidor personalizado para cada clase.

Intenté crear un convertidor genérico pero no pude encontrar la manera de identificarlo en el archivo web.config. Me encantaría saber si alguien más lo ha logrado.

Me surgió la idea al intentar resolver el problema anterior y tropecé con el "Serializador JSON .NET más preciso" de Nick Berardi (google it).

Funcionó para mí :)

Gracias a todos.

+0

eso es básicamente lo que estamos haciendo ... la configuración web tiene un convertidor genérico y la propiedad SupportedTypes está anulada. Las actualizaciones anteriores no son el código exacto que estamos usando, pero conceptualmente es lo mismo. –

Cuestiones relacionadas