簡體   English   中英

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

[英]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> (我想你可以看到我要去的地方......)

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

一行代碼,使用所有內置類型:)

嵌套的ExpandoObjects

當然,如果你正在嵌套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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM