2009-09-15 8 views
7

Tengo un modelo de vista personalizado que se serializó usando un JsonResult. ViewModel tiene algunas propiedades que deben ser públicas, pero al mismo tiempo estas propiedades no deberían estar visibles en la salida resultante de Json.¿Cómo puedo excluir algunas propiedades públicas de ser serializadas en un JsonResult?

Ya he intentado utilizar el atributo [NonSerialized], pero eso no pareció tener ningún efecto.

¿Hay alguna manera simple de hacer esto? ¿O tendría que codificar mi propio tipo de resultado (en cuyo caso probablemente no me moleste)?

+0

La mayor parte de la respuesta está usando Attribute o wraper cualquiera. Simplemente quiero excluir algunas de las propiedades públicas durante la serialización. Estoy buscando JSON.NET como lo sugiere @Charlino pero no encontré el camino. Este es el caso: tengo la propiedad 'Error' que solo se configurará cuando se produzca un error. el lado del cliente comprobará que este primero muestre el mensaje; de ​​lo contrario, mostrará el resto de la propiedad del modelo. Cuando no hay ningún error, se mostrará '{...," Error ": null}'! – CallMeLaNN

Respuesta

24

Puede poner un atributo [ScriptIgnore] en los miembros que no se deben serializar. Ver ScriptIgnoreAttribute Class in MSDN para un ejemplo.

+8

para otros por ahí el espacio de nombres completo del atributo es [System.Web.Script.Serialization.ScriptIgnore] – EBarr

+0

Parece que 'ScriptIgnore' cuando se utiliza el normal MVC' retorno Json() '' --- entonces JsonIgnore' si el uso de 'Json.NET' – mmcrae

0

No es exactamente la respuesta que está buscando, pero puede hacer trampa Json() usando el siguiente código y clases anónimas:

MyModel model = ...; 
return Json(new MyModel {model.Prop1, model.Prop2}); 
+0

Sé que puedo usar tipos anónimos en su lugar. Pero eso hace que las pruebas unitarias sean mucho más difíciles. Significa que tendría que analizar el resultado serializado o utilizar el reflejo. –

+0

Pruebas unitarias JsonResults es difícil para empezar. ¿Cómo estabas haciendo esto antes? No parece que puedas recuperar el modelo subyacente que condujo el JsonResult. –

+1

Eso no es problema en absoluto. la propiedad JsonResult.Data contiene el objeto que se serializará. Como utilizo un modelo de vista personalizado en lugar de un tipo de objeto anónimo, simplemente puedo tomar ese objeto y probar sus propiedades. –

0

Puede crear una clase contenedora que expone sólo aquellas propiedades que desee en el JsonResult. En el siguiente ejemplo, Cow tiene 2 propiedades: "Leg" y "Moo". Supongamos que quiere exponer solo "Leg" como una propiedad. Entonces

Dim cw como CowWrapper = Nueva CowWrapper (c)

devolverá una clase contenedora que sólo expone "Pierna". Esto también es útil para cosas como DataGridView si solo quieres mostrar algún subconjunto de las propiedades.

Public Class vaca

Pública de sólo lectura de la pierna de Propiedad() As String

get 

     return "leg" 

    end get 

extremo propiedad

Pública de sólo lectura MOO propiedad() As String

get 

     return "moo" 

    end get 

extremo propiedad

fin de la clase

Public Class CowWrapper

Private m_cow as Cow = Nothing 

Public Sub New(ByVal cow as Cow) 

    m_cow = cow 

end Sub 


    m_cow = cow 

Pública de sólo lectura de la pierna de Propiedad() As String

get 

     return m_cow.Leg() 

    end get 

extremo propiedad

Clase final

+0

Esto funciona, pero al precio de tener que copiar bastantes propiedades, así que no es realmente lo que estaba buscando. –

+0

¿A qué costo? ¿Programación? Debería ser simple escribir una utilidad simple para construir estas clases dado el archivo de clase como entrada, o escribir una clase que pueda construir las propiedades dinámicamente usando introspección. Tiempo de ejecución? El costo de una referencia adicional es insignificante. –

+0

No me di cuenta de que te refieres a construir tales tipos en tiempo de ejecución a través de la reflexión. Eso, por supuesto, cubriría todos mis tipos de viewmodel personalizados sin ninguna codificación adicional. Un enfoque válido, pero es demasiado complicado en comparación con el uso de una herramienta lista (es decir, Json.Net) –

1

extender la clase JavaScriptConverter a no incluir prope rties con el NonSerializedAttribute. Luego puede crear un ActionResult personalizado que use su JavaScriptConverter para serializar el objeto.

Esto crea una clase sólida y comprobable sin tener que (re) generar clases de envoltura ni usar objetos anónimos.

+0

Ese es el enfoque que también se me vino a la mente. Ciertamente funciona, pero como esto solo se trata de ahorrar unos pocos bytes de cada solicitud de Json, esperaba que hubiera algo más simple. Buen enfoque, pero simplemente demasiado trabajo en comparación con los beneficios en mi caso. –

2

Echa un vistazo a JSON.NET de James Newton-King. Hará lo que estás buscando.

+0

¡Perfecto! Como resultado, James también ha escrito una relación del tipo de JsonResult: http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx –

2

Simplemente cree una interfaz para devolver en lugar de una clase.

public interface IMyViewModel { 
    string MyPublicProperty { get; set; } 
} 

A continuación, cree una clase que hereda la interfaz

public class MyViewModel : IMyViewModel { 
    public string MyPublicProperty { get; set; } 
    public string MyNotSoPublicProperty { get; set; } 
} 

y devolver la interfaz, no a la clase, en el controlador de Acción

public JsonResult MyJson(){ 
    IMyViewModel model = new MyViewModel(); 
    return Json(model); 
} 

Y el JSON resultante será

{ 
    'MyPublicProperty': '' 
} 

Uno de los desafíos en las secuencias de comandos del lado del cliente es que, si está cambiando las clases, no tiene idea de si está destruyendo la implementación del lado del cliente o no. Si utiliza tipos de interfaz en su JSON, comprenderá que si cambia la interfaz, está haciendo algo que posiblemente mate la implementación del lado del cliente. Y también le evita duplicar la verificación del lado del cliente en vano si está cambiando algo que NO está en la interfaz (por lo que no se serializa).

También, muchas veces, sus ViewModels pueden tener grandes colecciones o tipos complejos en ellos que no necesariamente desea entregar al cliente. Puede llevar mucho tiempo serializar o exponer información que simplemente no pertenece al código del cliente. El uso de interfaces hará que sea más transparente saber qué es lo que está en la salida.

Además, el uso de atributos como [ScriptIgnore] en una propiedad solo se aplica a un escenario específico (Serialización de JavaScript), lo que le obliga a enfrentar exactamente el mismo problema si posteriormente lo serializa a XML, por ejemplo. Esto innecesariamente ensuciaría tus modelos de vista con toneladas de atributos. ¿Cuántos de ellos realmente quieres allí? El uso de intefaces se aplica en cualquier lugar y ningún modelo de vista debe estar repleto de atributos adicionales.

+0

Gracias por tu respuesta. No es un mal método, pero también hay una manera más automatizada para asegurar que sus clases del cliente todavía están en sincronía con el lado del servidor: El uso de imprenta para el código del lado del cliente y programación generar sus ViewModels del lado del cliente de objetos del lado del servidor durante la compilación. Ver http://type.litesolutions.net/ para más detalles. –

+0

¿Funcionaría este enfoque con una lista siendo serializada? No puedo hacerlo funcionar ... – tomasofen

Cuestiones relacionadas