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

IT技术 c# javascript asp.net-mvc json expandoobject
2021-01-26 14:53:59

我真的很喜欢在运行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而不是ExpandoObject--JsonResult能够将dynamic属性和值序列化为单个对象(没有键或值业务),我可以实现这一点但我需要使用的原因ExpandoObject是因为我不知道所有我想要在对象上的属性直到运行时,据我所知,如果dynamic不使用ExpandoObject.

我可能需要在我的 javascript 中筛选“关键”、“value”业务,但我希望在将其发送给客户之前弄清楚这一点。谢谢你的帮助!

6个回答

使用 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");
    }
}
Newtonsoft.Json 你是说?
2021-04-13 14:53:59
newtonsoft.json 可以更好地处理扩展或字典和内部字典中的递归扩展,开箱即用
2021-04-13 14:53:59

您还可以制作一个仅适用于 ExpandoObject 的特殊 JSONConverter,然后将其注册到 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);
这非常适合我的需求。如果有人想插入一些代码NotImplementedException来添加类似的东西serializer.Deserialize<ExpandoObject>(json);,@theburningmonk提供了一个对我有用的解决方案
2021-03-22 14:53:59
我发现最简单的方法是: new JavaScriptSerializer().Deserialize<object>(Newtonsoft.Json.JsonConvert.SerializeObject(listOfExpandoObject)); 你怎么看?
2021-03-22 14:53:59
我的序列化程序被递归调用。如果我设置 RecursionLimit,我要么得到递归限制超出错误或堆栈溢出异常错误。我该怎么办?:(
2021-03-22 14:53:59
伟大的工作@pablo。将自定义序列化例程插入 MVC 框架的优秀示例!
2021-03-23 14:53:59

这是我为实现您所描述的行为所做的工作:

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,将其转换为字典,像字典一样使用它,然后当它不够好时,将其转换为字典......为什么不直接使用字典这个案例?... o_o
2021-03-15 14:53:59
AnExpandoObject为您提供了比简单的字典更大的灵活性。尽管上面的示例没有演示,但您可以使用 的动态功能ExpandoObject在 JSON 中添加您想要的属性。普通Dictioanry对象会毫无问题地转换为 JSON,因此通过进行转换,这是一种简单的 O(n) 方法,可以将易于使用的动态ExpandoObject转换为可以 JSONified 的格式。不过你是对的,上面的例子是对ExpandoObject; 一个简单的Dictionary会好得多。
2021-03-28 14:53:59
“expando.Add”对我不起作用。我相信在这种情况下它是“d.Add”(对我有用)。
2021-03-29 14:53:59
好的。您还可以动态转换动态: return new JsonResult(((ExpandoObject)someIncomingDynamicExpando).ToDictionary(item => item.Key, item => item.Value))
2021-04-09 14:53:59
更喜欢这种方法 - 创建副本在任何环境中都不起作用,但我只有小对象,并且 Expando 由(不可更改的)第 3 方提供....
2021-04-12 14:53:59

我通过编写一个将 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);

我希望这有帮助!

我对该方法进行了一些改进,使其具有递归性。这是代码:gist.github.com/renanvieira/e26dc34e2de156723f79
2021-03-18 14:53:59
-1:在返回字符串的扩展方法中执行此操作不是将此行为与框架接口的正确方法。您应该扩展内置的序列化架构。
2021-03-29 14:53:59
这种方法的主要缺点是缺乏递归——如果你知道顶级对象是动态的,就是这样,这可行,但如果动态对象可能位于返回的对象树的任何或每个级别,这将失败。
2021-04-04 14:53:59
不要评价它!您应该使用 JSON 反序列化器来避免安全问题。参见 json2.js: json.org/js.html var o = JSON.parse(myJsonString);
2021-04-06 14:53:59
不过我喜欢这种扩展方法。好的!
2021-04-11 14:53:59

我能够使用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 " }

您还可以通过完成以下步骤,使用 JSON .Net (Newtonsoft) 执行此操作。var entity = 人为对象;var json = JsonConvert.SerializeObject(entity);
2021-03-18 14:53:59