【问题标题】:Parsing JSON without having to create tons of classes无需创建大量类即可解析 JSON
【发布时间】:2014-12-18 08:22:51
【问题描述】:

我目前正在编写一项服务,该服务应该轮询多个 API,尽可能统一数据,并将其存储在我的数据库中。我宁愿不必为我提出的每个请求以及我想从请求中保存的每个数据子集创建一个新类。所以我选择使用匿名和动态类型。这创造了以下怪物;可能是因为我很长时间没有使用匿名/动态类型了。我还应该注意到该功能不起作用,但应该很好地说明我想要实现的目标。

    public string GetActivities(ApplicationUser user)
    {
        //TODO: get statistics of today. If exists, overwrite.

        var date = DateTime.Today;
        var apiCall = String.Format("/1/user/-/activities/date/{0}.json", date.ToString("yyyy-MM-dd"));
        var request = new RestRequest(apiCall);
        var response = restClient.Execute(request);

        //If the response is not what we expected (and exception gets thrown in HandleResponse), rethrow the exception. 
        try
        {
            HandleResponse(response);
        }
        catch
        {
            throw;
        }

        //Create a dynamic object from the JSON response. This way we do not need to create a new class for each response.
        dynamic data = JsonConvert.DeserializeObject(response.Content);

        //Create a list to add anonymous objects to. We define the prototype in Select()
        var activities = Enumerable.Empty<dynamic>()
         .Select(r => new { 
             distance = 0, 
             duration = 0, 
             hasStartTime = false, 
             startTime = DateTime.Now, 
             calories = 0, 
             steps = 0 
         }).ToList();

        //Grab the data we need from the API response
        foreach(var a in data.activities)
        {
            var act = new
            {
                distance = a.distance,
                duration = a.duration,
                hasStartTime = a.hasStartTime,
                startTime = a.startTime,
                calories = a.calories,
                steps = a.steps
            };
            activities.Add(act);
        }

        List<Statistic> statistics = new List<Statistic>();

        foreach (var a in activities)
        {
            var parsedData = new { distance = "" };

            //Add the data we received as a JSON object to the object we store in the database.
            var statistic = new Statistic()
            {
                ID = Guid.NewGuid(),
                Device = context.Device.Where(d => d.Name == "Fitbit").Single(),
                Timestamp = DateTime.Today,
                Type = context.StatisticType.Where(s => s.Type == "calories_eaten").Single(),
                User = user,
                Value = JsonConvert.SerializeObject(parsedData)
            };

            statistics.Add(statistic);
        }

        //Save the newly added data to the database.
        context.Statistic.AddRange(statistics);

        context.SaveChanges();

        return null;
    }

考虑到这个功能已经变成了怪物,还有其他选择吗?最好是我不需要创建大量类的地方。

【问题讨论】:

  • 不创建类的替代方法是创建类。根据每个 API 中 JSON 的相似程度,您也许可以创建一些可重用的方法来处理它们,但是在不知道 JSON 是什么样子的情况下,真的没有任何办法可以说。我最好的建议是,让它发挥作用;然后让另一个工作,然后寻找相似之处并将这些部分分解为辅助方法。继续这样做,直到您将设计发展为适合您的东西。
  • @BrianRogers 我刚刚发现了 ExpandoObjects,它们似乎帮了很多忙。它帮助我将上述方法减少了 25 行。还有一些样板文件需要考虑。

标签: entity-framework parsing json.net


【解决方案1】:

你实际上想做什么?通过阅读您的代码,您似乎丢弃了所有有关活动的数据。 JSON 影响代码的唯一方式是活动的数量。

这是:

var parsedData = new { distance = "" };

其实应该是这样说的:

var parsedData = new { distance = a.distance };

如果您只对您阅读的 JSON 中的一个值感兴趣,我建议使用 JSON.net LINQ 会更简单。这是一个接口,它将 JSON 视为只是一堆字典(称为“对象”以与 JS 保持一致,其中对象实际上是字典)和数组,就像 JSON 的意图一样。

JObject data = JObject.Parse(response.Content);
JArray activities = (JArray)data["activities"];
foreach (JToken activity in activities)
{
    JObject activityObject = (JObject)activity;
    JObject parsedData = new JObject();
    parsedData["distance"] = activityObject["distance"];
    var statistic = new Statistic()
    {
        ...
        Value = parsedData.ToString();
    }
    ...
}

【讨论】:

    【解决方案2】:

    我可能会错过您正在尝试做的事情,但如果您使用的是 MVC,则不需要创建一个空的动态 Enumerable。您应该能够将带回的原始数据本身迭代到您可以在“模型”文件夹中创建的单个“活动”模型中:

    public class Activity {
             public int distance { get; set; };
             public int duration { get; set; }; 
             public bool hasStartTime { get; set; }; 
             public DateTime startTime { get; set; }; 
             public int calories { get; set; };
             public int steps { get; set; };
    }
    

    然后:

    List<Statistic> statistics = new List<Statistic>();
    IEnumerable<Activity> data = JsonConvert.DeserializeObject(response.Content);
    
    foreach(var a in data)
    {
        statistics.Add( new Statistic()
        {
                ID = Guid.NewGuid(),
                Device = context.Device.Where(d => d.Name == "Fitbit").Single(),
                Timestamp = DateTime.Today,
                Type = context.StatisticType.Where(s => s.Type == "calories_eaten").Single(),
                User = user,
                Value = JsonConvert.SerializeObject(parsedData)
                Distance = a.distance,
                Duration = a.duration,
                StartTime = a.startTime,
                Calories = a.calories,
                Steps = a.steps
         });
    }
    return statistics; // or do whatever you were going to do with this, here
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 2013-08-30
      • 1970-01-01
      • 2015-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多