serializar newtonsoft deserialize create c# javascript asp.net-mvc json expandoobject

c# - create - newtonsoft json deserialize expandoobject



¿Cómo aplanar un ExpandoObject devuelto a través de JsonResult en asp.net mvc? (12)

Esta es una respuesta tardía, pero tuve el mismo problema, y ​​esta pregunta me ayudó a resolverlos. Como resumen, pensé que debería publicar mis resultados, con la esperanza de que acelerara la implementación para otros.

Primero, ExpandoJsonResult, del que puedes devolver una instancia en tu acción. O puede anular el método Json en su controlador y devolverlo allí.

public class ExpandoJsonResult : JsonResult { public override void ExecuteResult(ControllerContext context) { HttpResponseBase response = context.HttpContext.Response; response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json"; response.ContentEncoding = ContentEncoding ?? response.ContentEncoding; if (Data != null) { JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoConverter() }); response.Write(serializer.Serialize(Data)); } } }

Luego, el convertidor (que admite serialización y deserialización. Vea a continuación un ejemplo de cómo deserializar).

public class ExpandoConverter : JavaScriptConverter { public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { return DictionaryToExpando(dictionary); } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { return ((ExpandoObject)obj).ToDictionary(x => x.Key, x => x.Value); } public override IEnumerable<Type> SupportedTypes { get { return new ReadOnlyCollection<Type>(new Type[] { typeof(System.Dynamic.ExpandoObject) }); } } private ExpandoObject DictionaryToExpando(IDictionary<string, object> source) { var expandoObject = new ExpandoObject(); var expandoDictionary = (IDictionary<string, object>)expandoObject; foreach (var kvp in source) { if (kvp.Value is IDictionary<string, object>) expandoDictionary.Add(kvp.Key, DictionaryToExpando((IDictionary<string, object>)kvp.Value)); else if (kvp.Value is ICollection) { var valueList = new List<object>(); foreach (var value in (ICollection)kvp.Value) { if (value is IDictionary<string, object>) valueList.Add(DictionaryToExpando((IDictionary<string, object>)value)); else valueList.Add(value); } expandoDictionary.Add(kvp.Key, valueList); } else expandoDictionary.Add(kvp.Key, kvp.Value); } return expandoObject; } }

Puede ver en la clase ExpandoJsonResult cómo usarlo para la serialización. Para deserializar, cree el serializador y registre el convertidor de la misma manera, pero use

dynamic _data = serializer.Deserialize<ExpandoObject>("Your JSON string");

Muchas gracias a todos los participantes que me ayudaron.

Me gusta mucho el ExpandoObject mientras ExpandoObject un objeto dinámico del lado del servidor en el tiempo de ejecución, pero estoy teniendo problemas para solucionar esto durante la serialización JSON. Primero, instanciar el objeto:

dynamic expando = new ExpandoObject(); var d = expando as IDictionary<string, object>; expando.Add("SomeProp", SomeValueOrClass);

Hasta aquí todo bien. En mi controlador MVC, quiero enviar esto como un JsonResult, así que hago esto:

return new JsonResult(expando);

Esto serializa el JSON en el siguiente, para ser consumido por el navegador:

[{"Key":"SomeProp", "Value": SomeValueOrClass}]

PERO, lo que realmente me gustaría es ver esto:

{SomeProp: SomeValueOrClass}

Sé que puedo lograr esto si utilizo dynamic lugar de ExpandoObject . JsonResult puede serializar las propiedades y los valores dynamic en un solo objeto (sin negocio Key o Value), pero la razón por la que necesito usar ExpandoObject es porque no Conozco todas las propiedades que quiero sobre el objeto hasta el tiempo de ejecución , y hasta donde yo sé, no puedo agregar dinámicamente una propiedad a una dynamic sin usar un ExpandoObject .

Puede que tenga que examinar el negocio "Clave", "Valor" en mi javascript, pero esperaba resolver esto antes de enviarlo al cliente. ¡Gracias por tu ayuda!


Esto es lo que hice para lograr el comportamiento que está describiendo:

dynamic expando = new ExpandoObject(); expando.Blah = 42; expando.Foo = "test"; ... var d = expando as IDictionary<string, object>; d.Add("SomeProp", SomeValueOrClass); // After you''ve added the properties you would like. d = d.ToDictionary(x => x.Key, x => x.Value); return new JsonResult(d);

El costo es que está haciendo una copia de los datos antes de serializarlos.


Esto puede no ser útil para usted, pero tenía un requisito similar, pero usé un SerializableDynamicObject

Cambié el nombre del diccionario a "Campos" y luego se serializa con Json.Net para producir json que se ve así:

{"Campos": {"Propiedad1": "Valor1", "Propiedad2": "Valor2", etc. donde Propiedad1 y Propiedad2 son Propiedades añadidas dinámicamente, es decir, Teclas de diccionario

Sería perfecto si pudiera deshacerme de la propiedad adicional "Campos" que encapsula el resto, pero he solucionado esa limitación.

Respuesta movida de esta pregunta a petición


Llevé el proceso de aplanamiento un paso más allá y revisé si había objetos en la lista, lo que elimina el absurdo del valor clave. :)

public string Flatten(ExpandoObject expando) { StringBuilder sb = new StringBuilder(); List<string> contents = new List<string>(); var d = expando as IDictionary<string, object>; sb.Append("{ "); foreach (KeyValuePair<string, object> kvp in d) { if (kvp.Value is ExpandoObject) { ExpandoObject expandoValue = (ExpandoObject)kvp.Value; StringBuilder expandoBuilder = new StringBuilder(); expandoBuilder.Append(String.Format("/"{0}/":[", kvp.Key)); String flat = Flatten(expandoValue); expandoBuilder.Append(flat); string expandoResult = expandoBuilder.ToString(); // expandoResult = expandoResult.Remove(expandoResult.Length - 1); expandoResult += "]"; contents.Add(expandoResult); } else if (kvp.Value is List<Object>) { List<Object> valueList = (List<Object>)kvp.Value; StringBuilder listBuilder = new StringBuilder(); listBuilder.Append(String.Format("/"{0}/":[", kvp.Key)); foreach (Object item in valueList) { if (item is ExpandoObject) { String flat = Flatten(item as ExpandoObject); listBuilder.Append(flat + ","); } } string listResult = listBuilder.ToString(); listResult = listResult.Remove(listResult.Length - 1); listResult += "]"; contents.Add(listResult); } else { contents.Add(String.Format("/"{0}/": {1}", kvp.Key, JsonSerializer.Serialize(kvp.Value))); } //contents.Add("type: " + valueType); } sb.Append(String.Join(",", contents.ToArray())); sb.Append("}"); return sb.ToString(); }


Lo resolví escribiendo un método de extensión que convierte el ExpandoObject en una cadena JSON:

public static string Flatten(this ExpandoObject expando) { StringBuilder sb = new StringBuilder(); List<string> contents = new List<string>(); var d = expando as IDictionary<string, object>; sb.Append("{"); foreach (KeyValuePair<string, object> kvp in d) { contents.Add(String.Format("{0}: {1}", kvp.Key, JsonConvert.SerializeObject(kvp.Value))); } sb.Append(String.Join(",", contents.ToArray())); sb.Append("}"); return sb.ToString(); }

Esto usa la excelente biblioteca de Newtonsoft .

JsonResult se ve así:

return JsonResult(expando.Flatten());

Y esto se devuelve al navegador:

"{SomeProp: SomeValueOrClass}"

Y puedo usarlo en javascript haciendo esto (referenciado here ):

var obj = JSON.parse(myJsonString);

¡Espero que esto ayude!


Parece que el serializador está transfiriendo el Expando a un diccionario y luego serialándolo (por lo tanto, el negocio clave / valor). ¿Has probado deserializar como un diccionario y luego devolverlo a un Expando?


Pude resolver este mismo problema usando JsonFx .

dynamic person = new System.Dynamic.ExpandoObject(); person.FirstName = "John"; person.LastName = "Doe"; person.Address = "1234 Home St"; person.City = "Home Town"; person.State = "CA"; person.Zip = "12345"; var writer = new JsonFx.Json.JsonWriter(); return writer.Write(person);

salida:

{"FirstName": "John", "LastName": "Doe", "Address": "1234 Home St", "City": "Ciudad natal", "Estado": "CA", "Zip": "12345 "}


Solo tuve el mismo problema y descubrí algo bastante raro. Si lo hago:

dynamic x = new ExpandoObject(); x.Prop1 = "xxx"; x.Prop2 = "yyy"; return Json ( new { x.Prop1, x.Prop2 } );

Funciona, pero solo si mi método usa el atributo HttpPost. Si uso HttpGet obtengo un error. Entonces mi respuesta solo funciona en HttpPost. En mi caso, fue una llamada de Ajax para poder cambiar HttpGet por HttpPost.


También podría hacer un JSONConverter especial que funcione solo para ExpandoObject y luego registrarlo en una instancia de JavaScriptSerializer. De esta forma, puedes serializar arreglos de expando, combinaciones de objetos expandidos y ... hasta que encuentres otro tipo de objeto que no se esté serializando correctamente ("de la manera que quieras"), entonces fabricas otro convertidor o agregas otro tipo de éste. Espero que esto ayude.

using System.Web.Script.Serialization; public class ExpandoJSONConverter : JavaScriptConverter { public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer) { throw new NotImplementedException(); } public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer) { var result = new Dictionary<string, object>(); var dictionary = obj as IDictionary<string, object>; foreach (var item in dictionary) result.Add(item.Key, item.Value); return result; } public override IEnumerable<Type> SupportedTypes { get { return new ReadOnlyCollection<Type>(new Type[] { typeof(System.Dynamic.ExpandoObject) }); } } }

Usando convertidor

var serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter()}); var json = serializer.Serialize(obj);


Usando JSON.NET puede llamar a SerializeObject para "aplanar" el objeto de expansión:

dynamic expando = new ExpandoObject(); expando.name = "John Smith"; expando.age = 30; var json = JsonConvert.SerializeObject(expando);

Se producirá:

{"name":"John Smith","age":30}

En el contexto de un Controlador ASP.NET MVC, el resultado puede devolverse utilizando el método de contenido:

public class JsonController : Controller { public ActionResult Data() { dynamic expando = new ExpandoObject(); expando.name = "John Smith"; expando.age = 30; var json = JsonConvert.SerializeObject(expando); return Content(json, "application/json"); } }


Utilizando el despliegue de ExpandoObject dinámico de WebApi en ASP.Net 4, el formateador JSON predeterminado parece aplanar ExpandoObjects en un objeto JSON simple.


JsonResult utiliza JavaScriptSerializer que en realidad deserializa (el concreto) Dictionary<string, object> como lo desee.

Hay una sobrecarga del constructor Dictionary<string, object> que toma IDictionary<string, object> .

ExpandoObject implementa IDictionary<string, object> (creo que se puede ver hacia dónde voy aquí ...)

Nivel único ExpandoObject

dynamic expando = new ExpandoObject(); expando.hello = "hi"; expando.goodbye = "cya"; var dictionary = new Dictionary<string, object>(expando); return this.Json(dictionary); // or new JsonResult { Data = dictionary };

Una línea de código, usando todos los tipos incorporados :)

Anidados ExpandoObjects

Por supuesto, si anida ExpandoObject s, necesitará convertirlos recursivamente en Dictionary<string, object> s:

public static Dictionary<string, object> RecursivelyDictionary( IDictionary<string, object> dictionary) { var concrete = new Dictionary<string, object>(); foreach (var element in dictionary) { var cast = element.Value as IDictionary<string, object>; var value = cast == null ? element.Value : RecursivelyDictionary(cast); concrete.Add(element.Key, value); } return concrete; }

su código final se está convirtiendo

dynamic expando = new ExpandoObject(); expando.hello = "hi"; expando.goodbye = "cya"; expando.world = new ExpandoObject(); expando.world.hello = "hello world"; var dictionary = RecursivelyDictionary(expando); return this.Json(dictionary);