在asp.net mvc中如何平铺通过JsonResult返回的ExpandoObject?

在编译运行时的服务器端dynamic对象时,我非常喜欢ExpandoObject ,但是我在JSON序列化过程中遇到了麻烦。 首先,我实例化对象:

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

到现在为止还挺好。 在我的MVC控制器中,我想将其作为JsonResult发送,所以我这样做:

 return new JsonResult(expando); 

这将JSON序列化到下面,供浏览器使用:

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

但是,我真的很想看到这个:

 {SomeProp: SomeValueOrClass} 

我知道我可以实现这一点,如果我使用dynamic而不是ExpandoObjectJsonResult能够将dynamic属性和值序列化到一个对象(没有键或值业务), 但我需要使用ExpandoObject的原因是因为我不直到运行时才知道对象上的所有属性 ,而据我所知,我不能dynamic地将属性添加到dynamic而不使用ExpandoObject

我可能不得不在JavaScript中筛选“Key”,“Value”业务,但是我希望在把它发送给客户端之前弄清楚。 谢谢你的帮助!

您也可以创build一个仅用于ExpandoObject的特殊JSONConverter,然后将其注册到JavaScriptSerializer的一个实例中。 这样你可以序列化数组expando,扩展对象的组合和…直到你find另一种没有正确序列化的对象(“你想要的方式”),然后你做另一个转换器,或添加另一种types这个。 希望这可以帮助。

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

使用转换器

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

使用JSON.NET,你可以调用SerializeObject来“展平”expando对象:

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

会输出:

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

在ASP.NET MVC控制器的上下文中,可以使用Content-method返回结果:

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

以下是我所做的,以实现您所描述的行为:

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

成本是在序列化之前制作数据的副本。

我通过编写将ExpandoObject转换为JSONstring的扩展方法解决了这个问题:

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

这使用优秀的Newtonsoft库。

JsonResult然后看起来像这样:

 return JsonResult(expando.Flatten()); 

这是返回到浏览器:

 "{SomeProp: SomeValueOrClass}" 

我可以在JavaScript中使用它( 这里引用):

 var obj = JSON.parse(myJsonString); 

我希望这有帮助!

我能用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); 

输出:

{“FirstName”:“John”,“LastName”:“Doe”,“Address”:“1234 Home St”,“City”:“家乡”,“State”:“CA”,“Zip” “}

我进一步扁平化过程,并检查列表对象,这将删除键值废话。 🙂

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

这可能对你没有用处,但是我有类似的要求,但是使用了SerializableDynamicObject

我把字典的名字改为“Fields”,然后用Json.Net序列化,产生如下的json:

{“Fields”:{“Property1”:“Value1”,“Property2”:“Value2”等等,其中Property1和Property2是dynamic添加的属性 – 即字典键

如果我能摆脱封装其余的额外的“领域”属性将是完美的,但我已经解决了这个限制。

根据要求从这个问题转移回答

这是一个迟到的答案,但我有同样的问题,这个问题帮助我解决它们。 作为总结,我想我应该公布我的结果,希望能够加快实施。

首先是ExpandoJsonResult,你可以在你的行为中返回一个实例。 或者你可以重写控制器中的Json方法,并将其返回。

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

然后是转换器(同时支持序列化和反序列化,下面是一个反序列化的例子)。

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

您可以在ExpandoJsonResult类中看到如何使用它进行序列化。 为了反序列化,创build串行器并以相同的方式注册转换器,但是使用

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

非常感谢,对于这里帮助我的所有参与者。

使用从ASP.Net 4中的WebApi中返回dynamicExpandoObject,默认的JSON格式化器似乎将ExpandoObjects变为简单的JSON对象。

JsonResult使用实际反序列化(具体) Dictionary<string, object> JavaScriptSerializer

Dictionary<string, object>构造函数的重载需要使用IDictionary<string, object>

ExpandoObject实现了IDictionary<string, object> (我想你可以看到我要去哪里…)

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

一行代码,使用所有内置types:)

嵌套的ExpandoObjects

当然,如果您嵌套ExpandoObject那么您将需要recursion地将它们全部转换为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; } 

你的最终代码变成了

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

似乎序列化程序将Expando转换为字典,然后序列化(因此键/值业务)。 你有没有尝试反序列化作为一个词典,然后把它回到一个Expando?

我只是有同样的问题,并找出一些非常奇怪的东西。 如果我做:

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

它的工作原理,但只有当我的方法使用HttpPost属性。 如果我使用HttpGet我得到错误。 所以我的答案只适用于HttpPost。 在我的情况下,这是一个Ajax调用,所以我可以通过HttpPost改变HttpGet。