如何自定义JSON对象序列化?

时间:2014-08-12 13:47:06

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

我有一个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]"
  }
]

我首先设计数据库,然后返回带有日志的User对象,这样就返回了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感到困惑,但也许这是解决这个问题的方法?

1 个答案:

答案 0 :(得分:2)

我认为你有两种选择:

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

    然后您调整代码以序列化一堆LogViewModel代替:

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

    上述映射代码可能变得乏味 - 这就是AutoMapper等工具的用武之地。

    您也可以投射包含您关注的属性的匿名类型,但效果类似,但我发现管理ViewModel要容易得多。

  3. 我建议使用ViewModel路线,特别是因为它可以很好地区分问题并保护您免受人们在系统的这个特定区域添加您不关心的属性的问题。