【问题标题】:How do I customize JSON object serialization?如何自定义 JSON 对象序列化?
【发布时间】:2014-08-12 13:47:05
【问题描述】:

我有一个 ASP.NET Razor 页面,我在其中尝试使用 jQuery ajax 请求显示日志列表。根据我在文档和论坛中的理解,我提出了这个 ajax 请求:

$.ajax({
    url: '@Url.Action("GetLogs","Logs")',
    dataType: "json",
    contentType :"application/json; charset=utf-8",
    success: function (data) {
        console.log("success");
        console.log(data);
    },
    error: function (msg) {
        console.log("error");
        console.log(msg);
    }
});

所以这行得通,但它返回的 json 并不是我想要的。以下是 ajax 请求(和 getAllLogs 函数)调用的控制器代码:

- LogsController -
public JsonResult GetLogs()
{
    List<Log> _loglist = LogsModel.getAllLogs(UserModel.getUser(Session["FirstName"].ToString(), Session["LastName"].ToString()));

    String json = JsonConvert.SerializeObject(_loglist, Formatting.Indented, new JsonSerializerSettings
    {
        ReferenceLoopHandling = ReferenceLoopHandling.Ignore

    });

    return Json(json, JsonRequestBehavior.AllowGet);
}

- LogsModel -
//get all logs to display in admin tab
public static List<Log> getAllLogs(User _user)
{
    // if user is admin we can show logs concerning all users
    if(UserModel.isAdmin(_user))
        return db.Logs.Where(log => log.Action.Equals("Set Characteristic")).OrderByDescending(log => log.Id).ToList();
    else
        //user is manager and will see only users in same plant
        return db.Logs.Where(log => log.User.Plant.Code.Equals(_user.Plant.Code) && log.Action.Equals("Set Characteristic")).OrderByDescending(log => log.Id).ToList();
        }

当我遇到自引用循环问题时,我使用了我发现的(referenceloophandling)。我不再有它的问题,但正如我之前所说,返回的字符串不是我想要的。

getAllLogs() 函数根据用户权限返回日志。这里它只返回 1 个 Log 对象(用于测试目的),并且这个唯一的日志应该在 json 中序列化,格式如下:

[
  {
    "Id" : 1,
    "User_Id": 1,
    "Date": "2014-08-11T10:00:00.000",
    "Action": "Set Characteristic",
    "State": true,
    "Message" : "User changed characteristic [charac_name]"
  }
]

问题是我首先设计了数据库,用户对象与日志一起返回,所以这是返回的 Json:

[
  {
    "User": {
      "Characteristic_Value": [],
      "Logs": [
        {
          "Id": 1,
          "User_Id": 1,
          "Date": "2014-08-11T14:11:52.523",
          "Action": "Login attempt",
          "State": true,
          "Message": "User successfully logged in"
        },
        {
          "Id": 2,
          "User_Id": 1,
          "Date": "2014-08-11T14:13:45.07",
          "Action": "Login attempt",
          "State": true,
          "Message": "User successfully logged in"
        },
        {
          "Id": 3,
          "User_Id": 1,
          "Date": "2014-08-11T14:15:07.043",
          "Action": "Login attempt",
          "State": true,
          "Message": "User successfully logged in"
        },
    Other User Data ...
    },
    "Id" : 21,
    "User_Id": 1,
    "Date": "2014-08-11T10:00:00.000",
    "Action": "Set Characteristic",
    "State": true,
    "Message" : "User changed characteristic [charac_name]"
  }
]

我得到了所有的用户数据。我是唯一一个在这个项目上工作的人,所以现在获取数据并不需要很长时间,但在不久的将来,会有更多的用户,查询的数据量将永远不会停止增加,而且我'我非常关心响应时间。

所以最后我的问题是:是否有可能摆脱与日志一起返回的用户数据?

我仍然对 JsonSerializerSettings 感到困惑,但也许这是解决这个问题的方法?

【问题讨论】:

  • 你为什么不在你的 linq 中 Select 日志
  • 我不确定你的意思。但我认为这就是我在 getAllLogs 函数中所做的。事情与实体框架有关,因为我在日志中有一个指向用户表的外键,对于我得到的每个日志,我都会得到所有用户信息。不知道我能不能解释清楚...

标签: c# ajax asp.net-mvc entity-framework json.net


【解决方案1】:

我认为你有两个选择:

  1. 将不想序列化的属性标记为[JsonIgnore]using an attribute。如果您的 Log 类位于一个真正不应该关心 JSON 序列化的不同层,这可能不是一个好选择。

  2. 创建ViewModels 来表示您要做想要发送的信息,可选择使用AutoMapper 之类的工具将类映射在一起。

    例如,LogViewModel 可能如下所示:

    public class LogViewModel
    {
        public int Id { get; set; }
    
        public int User_Id { get; set; }
    
        public DateTime Date { get; set; }
    
        public string Action { get; set; }
    
        public bool State { get; set; }
    
        public string Message { get; set; }
    }
    

    然后你可以调整你的代码来序列化一堆LogViewModels:

    IEnumerable<LogViewModel> viewModels =
        _logList.Select(l => new LogViewModel
        {
            Id = l.Id,
            User_Id = l.User_Id
            /* etc. */
        });        
    
    String json = JsonConvert.SerializeObject(viewModels);
    

    上面的映射代码可能会变得乏味——这就是像AutoMapper 这样的工具的用武之地。

    您也可以只投影一个包含您关心的属性的匿名类型,但我发现管理 ViewModel 会容易得多。

我建议采用 ViewModel 路线,特别是因为它很好地分离了关注点,并且当人们在系统的这个特定区域添加你不关心的属性时,它可以保护你免受问题的影响。

【讨论】:

  • 1.我没有 Log 类,每个类都是基表,所以我不能使用注释。 2. 我要试试这个 :) 通过它在论坛和东西上徘徊但并没有真正理解它,我认为 ViewModels 与 MVC 不兼容
  • ViewModels 只是一种模式,它们在 ASP.NET MVC 中被广泛使用(尽管它们确实不同于“ViewModel”在 MVVM 中的含义)
  • 我刚刚安装了 automapper 并创建了 LogsViewModel,它工作得非常好 :) 非常感谢!
  • @Nydieven:没问题!很高兴能提供帮助。
猜你喜欢
  • 2014-05-19
  • 2020-07-05
  • 2021-06-24
  • 2014-03-03
  • 2011-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多