Deserializar JSon a tipo anónimo .Net

En ciertas ocasiones podemos necesitar deserializar un objeto pasado desde un cliente en formato JSon a un tipo .net anónimo. Por ejemplo si utilizas MongoDB con el driver NORM y pretendes filtrar u ordenar de una manera dinámica, es difícil pasar estos objetos a la query para poder generar un filtro dinámico.

Para hacernos una idea esta sería una consulta paginada y  con un filtro sencillo con NORM:

//NoRM anonymous objects:
return GetRepository().GetCollection("Personas")
   .Find(
          new { Nombre = Q.IsNotNull() }, //Where
          new { Fecha = OrderBy.Descending }, //OrderBy
          10, // Nº elementos de la secuencia "Skip"
           0 // Primer elemento de la secuencia "Take"
);

Esta consulta nos devolvería las 10 primeras personas con Nombre que no sea null.

Tanto al utilizar los tipos anónimos como en las consultas LinQ se tiene que especificar la propiedad por la que se quiere filtrar u ordenar y eso nos limita a la hora de intentar hacer estas consultas de forma dinámica.

Parara hacer una prueba crearemos un tipo específico para utilizar en las consultas desde javaScript.

Lo ideal sería:

public class QueryRequest
{
    public int Pagina { get; set; }
    public int Registros { get; set; }
    public object Orden { get; set; }
    public object Filtro { get; set; }
}

Con este tipo nos pasarían como parámetros lel número de página, la cantidad de registros y además un object para  filtrar y otro object para ordenar.

Pero esto no funciona porque  NORM no lo reconoce como tipo anónimo y si lo intentamos pasar como string tampoco funciona.

Ahora solo nos queda intentar deserializar directamente nosotros el objeto JSON en un tipo anónimo y para eso utilizaremos el tipo dynamic que tenemos disponible desde c# 4.

Para eso solo tenemos que deserializar un texto con datos en formato JSon utilizandoJavaScriptSerializerque te permite utilizar un tipo T.

var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
var ObjetoAnonimo = jss.Deserialize(strJson);

Con estas dos líneas lo tenemos solucionado y el nuevo tipo podría quedar de esta manera para facilitar la entrada de los datos como string y la salida como objeto dinámico.

    public class QueryRequest
    {
        public int Pagina { get; set; }
        public int Registros { get; set; }

        // input string - output dynamic
        private string orden;
        public dynamic Orden { get { return DeserializarJSon(orden); } set { orden = value; } }

        // input string - output dynamic
        private string filtro;
        public dynamic Filtro { get { return DeserializarJSon(filtro); } set { filtro = value; } }

        private dynamic DeserializarJSon(string strJson)
        {
            var jss = new JavaScriptSerializer();
            dynamic resultado;
            try
            {
                resultado = jss.Deserialize(strJson);
            }
            catch (Exception)
            {
                resultado = new { };
            }
            return resultado;
        }

    }

Ahora nuestro objeto es capaz de devolver un tipo anónimo al consultar la propiedad Filtro y Orden.

 public IEnumerable FindFiltradoDinamico( QueryRequest datos )
        {
            //NoRM anonymous objects:
            return GetRepository().GetCollection("Personas")
                .Find(
                         datos.Filtro as object,
                         datos.Orden as object,
                         datos.Registros,
                         datos.Pagina * datos.Registros
                     );

        }

Ahora si tenemos las consultas preparadas para aceptar parámetros dinámicos.

        public ActionResult Index()
        {
            var datos = new QueryRequest()
            {
                Pagina = 0,
                Registros = 10,
                Filtro = " { Edad : { '$gt': 15 } } ", //mayor de 15 años
                Orden = "{ Nombre : -1}" // 1 Ascendente - -1 Descendente
            };

            var f2 = repositoryPersona.FindFiltradoDinamico(datos);

            return View();
        }

Deja un comentario