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

Tim*_*Dog 93 javascript c# asp.net-mvc json expandoobject

我非常喜欢在运行ExpandoObject时编译服务器端动态对象,但是在JSON序列化期间我遇到了麻烦.首先,我实例化对象:

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

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

return new JsonResult(expando);
Run Code Online (Sandbox Code Playgroud)

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

[{"Key":"SomeProp", "Value": SomeValueOrClass}]
Run Code Online (Sandbox Code Playgroud)

但是,我真正喜欢的是看到这个:

{SomeProp: SomeValueOrClass}
Run Code Online (Sandbox Code Playgroud)

我知道我可以实现这个,如果我使用dynamic而不是ExpandoObject- JsonResult能够将dynamic属性和值序列化为单个对象(没有Key或Value业务),但我需要使用的原因ExpandoObject是因为我不知道所有的直到运行时我想要的对象属性,据我所知,我不能动态地添加属性dynamic而不使用ExpandoObject.

我可能不得不在我的javascript中筛选"关键","价值"业务,但我希望在将其发送给客户之前解决这个问题.谢谢你的帮助!

Mik*_*nen 67

使用JSON.NET,您可以调用SerializeObject来"展平"expando对象:

dynamic expando = new ExpandoObject();
expando.name = "John Smith";
expando.age = 30;

var json = JsonConvert.SerializeObject(expando);
Run Code Online (Sandbox Code Playgroud)

将输出:

{"name":"John Smith","age":30}
Run Code Online (Sandbox Code Playgroud)

在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");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • newtonsoft.json可以更好地处理expandos或词典和内部词典中的递归expandos,开箱即用 (3认同)

小智 36

您还可以创建一个特殊的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) });
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用转换器

var serializer = new JavaScriptSerializer(); 
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoJSONConverter()});
var json = serializer.Serialize(obj);
Run Code Online (Sandbox Code Playgroud)

  • 这对我的需求非常有用.如果有人想为`NotImplementedException`插入一些代码来添加类似`serializer.Deserialize <ExpandoObject>(json);`,@ theburningmonk [提供解决方案](http://theburningmonk.com/2011/05/ idictionarystring-object-to-expandoobject-extension-method /)对我有用. (2认同)
  • 很棒的工作@ pablo.将自定义序列化例程插入MVC框架的优秀示例! (2认同)

ajb*_*ajb 25

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

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);
Run Code Online (Sandbox Code Playgroud)

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

  • 所以等等......你正在创建一个ExpandoObject,将其作为字典投射,像字典一样使用它,然后当它不够好时,将其转换为字典......为什么不在字典中使用这个案例?...... o_o (9认同)
  • `ExpandoObject`比简单的Dictionary更灵活.虽然上面的示例没有演示它,但您可以使用`ExpandoObject`的动态功能来添加您希望在JSON中拥有的属性.一个普通的`Dictioanry`对象将毫无问题地转换为JSON,因此通过转换,将易于使用的动态`ExpandoObject`放入可以JSONified的格式是一种简单的O(n)方法.你是正确的,上面的例子是对"ExpandoObject"的重复使用; 一个简单的`词典'会好得多. (5认同)

Tim*_*Dog 11

我通过编写一个将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();
}
Run Code Online (Sandbox Code Playgroud)

这使用了优秀的Newtonsoft库.

然后JsonResult看起来像这样:

return JsonResult(expando.Flatten());
Run Code Online (Sandbox Code Playgroud)

这将返回给浏览器:

"{SomeProp: SomeValueOrClass}"
Run Code Online (Sandbox Code Playgroud)

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

var obj = JSON.parse(myJsonString);
Run Code Online (Sandbox Code Playgroud)

我希望这有帮助!

  • 不要评估它!您应该使用JSON反序列化器来避免安全问题.请参阅json2.js:http://www.json.org/js.html var o = JSON.parse(myJsonString); (7认同)
  • -1:在返回字符串的扩展方法中执行此操作不是将此行为与框架接口的正确方法.您应该扩展内置的序列化体系结构. (3认同)

Gar*_*eld 5

我能够使用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);
Run Code Online (Sandbox Code Playgroud)

输出:

{"FirstName":"John","LastName":"Doe","Address":"1234 Home St","City":"Home Town","State":"CA","Zip":"12345 "}