[英]How to flatten an ExpandoObject returned via JsonResult in asp.net mvc?
在運行時編譯服務器端動態對象時,我非常喜歡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}
, and as far as I know, I cannot dynamically add a property to a dynamic
without using an ExpandoObject
. 我知道如果我使用dynamic
而不是ExpandoObject
我可以實現這一點JsonResult
能夠將dynamic
屬性和值序列化為單個對象(沒有Key或Value業務), ,據我所知,我不能動態地將屬性添加到dynamic
而不使用ExpandoObject
。
我可能不得不在我的javascript中篩選“Key”,“Value”業務,但我希望在將其發送給客戶端之前將其弄清楚。 謝謝你的幫助!
使用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");
}
}
您還可以創建一個特殊的JSONConverter,它只適用於ExpandoObject,然后在JavaScriptSerializer的實例中注冊它。 這樣你就可以序列化expando的數組,expando對象的組合......直到找到另一種沒有正確序列化的對象(“你想要的方式”),然后你創建另一個轉換器,或者添加另一個類型這個。 希望這可以幫助。
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);
以下是我為實現您所描述的行為所做的工作:
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轉換為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();
}
這使用了優秀的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”:“Home Town”,“State”:“CA”,“Zip”:“12345 “}
我進一步進行了展平處理並檢查列表對象,這樣就刪除了鍵值廢話。 :)
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是動態添加的屬性 - 即Dictionary Keys
如果我能擺脫其他封裝其余部分的“Fields”屬性,那將是完美的,但我已經解決了這個限制。
根據要求回答了這個問題
這是一個遲到的答案,但我有同樣的問題,這個問題幫我解決了。 作為總結,我認為我應該發布我的結果,希望它能加快其他人的實施。
首先是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類中看到如何使用它進行序列化。 要反序列化,請創建序列化器並以相同的方式注冊轉換器,但請使用
dynamic _data = serializer.Deserialize<ExpandoObject>("Your JSON string");
非常感謝所有幫助我的參與者。
在ASP.Net 4中使用從WebApi返回的動態ExpandoObject,默認的JSON格式化程序似乎將ExpandoObjects展平為簡單的JSON對象。
JsonResult
使用JavaScriptSerializer
實際上反序列化(具體) Dictionary<string, object>
。
Dictionary<string, object>
構造函數有一個重載,它接受IDictionary<string, object>
。
ExpandoObject
實現IDictionary<string, object>
(我想你可以看到我要去的地方......)
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 };
一行代碼,使用所有內置類型:)
當然,如果你正在嵌套ExpandoObject
那么你需要遞歸地將它們全部轉換為Dictionary<string, object>
:
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轉換為Dictionary,然后將其序列化(因此鍵/值業務)。 您是否嘗試過反序列化為詞典,然后將其轉換回Expando?
我只是遇到了同樣的問題並想出了一些非常奇怪的東西。 如果我做:
dynamic x = new ExpandoObject();
x.Prop1 = "xxx";
x.Prop2 = "yyy";
return Json
(
new
{
x.Prop1,
x.Prop2
}
);
它工作,但只有我的方法使用HttpPost屬性。 如果我使用HttpGet我得到錯誤。 所以我的回答只適用於HttpPost。 在我的情況下,它是一個Ajax調用,所以我可以通過HttpPost更改HttpGet。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.