【问题标题】:Reading JSON data in groups分组读取 JSON 数据
【发布时间】:2019-05-24 16:32:45
【问题描述】:

我有 JSON 数据(有汽车的家庭),我正在尝试读取 JSON 数据。一个子集如下所示:

[
   {
      "car":[
         "Honda Civic",
         "Toyota Camry"
      ]
   },
   {
      "car":[
         "Honda Civic"
      ]
   },
   {
      "car":[
         "BMW 760",
         "Mercedes S",
         "Smart Car"
      ]
   },
   {
      "car":[
         "Honda Odyssey",
         "Tesla X"
      ]
   },
   {
      "car":[
         "BMW 760"
      ]
   },
   {
      "car":[
         "Honda Odyssey",
         "Tesla X"
      ]
   },
   {
      "car":[
         "BMW 760"
      ]
   },
   {
      "car":[
         "Toyota Camry",
         "Honda Civic"
      ]
   }
]

当我使用以下逻辑读取文件时,它被成功读取。 (我使用的是Newtonsoft.Json。)

string sJSON = File.ReadAllText(@"D:\MyFolder\cars.json");
List<Car> allCars = JsonConvert.DeserializeObject<List<Car>>(sJSON);

Cars 类是这样的:

public class Car
{
    private ArrayList carNames = new ArrayList();

    public void AddCar(string carName)
    {
        carNames.Add(carName);
    }
}

我面临两个问题:

  1. 虽然 JSON 已成功读取,并且可以识别汽车名称,但未将它们正确添加到 allCars
  2. 如何计算汽车数量?例如:

    • 只有 BMW 760 的家庭是 3 个
    • 与思域和凯美瑞是2
    • 只有 Civic 是 1,等等。

我尝试执行this question 中提到的操作,但没有成功。

【问题讨论】:

  • 为什么要使用数组列表
  • @KunalMukherjee 因为我可以动态构建它,并且认为它可能有助于添加汽车。
  • "只有BMW 760的家庭"是2个。2个家庭只有2个,但3个家庭至少有1个。

标签: c# json parsing


【解决方案1】:

您必须首先展平嵌套的汽车名称列表,然后将它们分组以获得所需的输出。

class Program
{
    static void Main(string[] args)
    {
        string carsData = @"
                        [
               {
                  'car':[
                     'Honda Civic',
                     'Toyota Camry'
                  ]
               },
               {
                  'car':[
                     'Honda Civic'
                  ]
               },
               {
                  'car':[
                     'BMW 760',
                     'Mercedes S',
                     'Smart Car'
                  ]
               },
               {
                  'car':[
                     'Honda Odyssey',
                     'Tesla X'
                  ]
               },
               {
                  'car':[
                     'BMW 760'
                  ]
               },
               {
                  'car':[
                     'Honda Odyssey',
                     'Tesla X'
                  ]
               },
               {
                  'car':[
                     'BMW 760'
                  ]
               },
               {
                  'car':[
                     'Toyota Camry',
                     'Honda Civic'
                  ]
               }
            ]
        ";

        List<Car> allCars = JsonConvert.DeserializeObject<List<Car>>(carsData);

        // Flatten all the car names first then group them
        var carDistributions = allCars.SelectMany(x => x.CarNames)
               .GroupBy(x => x, x => x, (key, x) => new
               {
                   CarName = key,
                   Count = x.Count()
               })
               .ToList();

        foreach (var carDistribution in carDistributions)
        {
            Console.WriteLine(carDistribution.CarName + " " + carDistribution.Count);
        }


    }
}

public class Car
{
    [JsonProperty("Car")]
    public List<string> CarNames { get; set; }
}

输出:

Honda Civic 3
Toyota Camry 2
BMW 760 3
Mercedes S 1
Smart Car 1
Honda Odyssey 2
Tesla X 2

【讨论】:

    【解决方案2】:

    首先使用json2csharp 或PasteSpecial 选项来创建用于json 解析的模型。

    PasteSpecial 选项您可以在 Edit -> Paste Special -> Paste JSON As Classes 中找到

    这将为您提供正确的模型来解析您的 json 字符串,即

    public class Car
    {
        public List<string> car { get; set; }
    }
    

    现在,当您使用 Json 反序列化代码时,请使用相同的代码。

     List<Car> allCars = JsonConvert.DeserializeObject<List<Car>>(sJSON);
    

    使用 Linq SelectMany()where(),您可以根据其名称获取所有汽车记录,现在使用简单的 Count(),您将从 Json Array 中获取每辆汽车的计数

     int count = allCars.SelectMany(x => x.car).Where(x => x == "Honda Civic").Count(); // It will return 3 as a result
    

    【讨论】:

      【解决方案3】:

      此 JSON 结果的本机数据类型是 List&lt;Dictionary&lt;string, string[]&gt;&gt;。可以直接解析:

      var cars = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, string[]>>>(json);
      

      然后您可以创建一些函数来搜索该数据:

      private int HouseholdWith(List<Dictionary<string, string[]>> cars, string car1)
      {
          return cars.Count(household => household["car"].Any(c => c == car1));
      }
      
      private int HouseholdWith(List<Dictionary<string, string[]>> cars, string car1, string car2)
      {
          return cars.Count(household => household["car"].Any(c => c == car1) && household["car"].Any(c => c == car2));
      }
      
      private int HouseholdWithOnly(List<Dictionary<string, string[]>> cars, string car)
      {
          return cars.Count(household => household["car"].All(c => c == car));
      }
      

      如果您想将 JSON 中的数据重新组织到 Households 中,您可以执行以下操作:

      class Household
      {
          public List<string> Cars { get; set; }
      }
      
      var cars = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Dictionary<string, string[]>>>(json);
      List<Household> households = (
          from h in cars
          select new Household()
          {
              Cars = h["car"].ToList()
          }
      ).ToList();
      

      使用修改后的搜索功能:

      private int HouseholdsWith(List<Household> households, string car1)
      {
          return households.Count(h => h.Cars.Any(c => c == car1));
      }
      
      private int HouseholdsWith(List<Household> households, string car1, string car2)
      {
          return households.Count(h => h.Cars.Any(c => c == car1) && h.Cars.Any(c => c == car2));
      }
      
      private int HouseholdsWithOnly(List<Household> households, string car)
      {
          return households.Count(h => h.Cars.All(c => c == car));
      }
      

      然后进行测试:

      Console.WriteLine("Households who have only BMW 760 are {0}", HouseholdsWithOnly(households, "BMW 760"));
      //Households who have only BMW 760 are 2
      
      Console.WriteLine("Households who have BMW 760 are {0}", HouseholdsWith(households, "BMW 760"));
      //Households who have BMW 760 are 3
      
      Console.WriteLine("Households with Civic and Camry are {0}", HouseholdsWith(households, "Honda Civic", "Toyota Camry"));
      //Households with Civic and Camry are 2
      
      Console.WriteLine("Households with only Civic is {0}", HouseholdsWithOnly(households, "Honda Civic"));
      //Households with only Civic is 1
      

      恕我直言,您的 Newtonsoft 反序列化类应该尽可能简单。 Newtonsoft 很强​​大,在反序列化的过程中可以做很多事情,但是越简单,如果以后数据结构需要改变,修改也就越容易。反序列化后的映射函数是将数据转换为对应用程序有用的东西。我认为这是一个很好的 SoC 原理。

      奖金回合

      private void CreateReport(List<Household> households)
      {
          //get all unique cars
          List<string> cars = households.SelectMany(h => h.Cars).Distinct().OrderBy(c => c).ToList();
          foreach(string c in cars)
          {
              Console.WriteLine("Households with {0}: {1}", c, HouseholdsWith(households, c));
              Console.WriteLine("Households with only {0}: {1}", c, HouseholdsWithOnly(households, c));
          }
      
          //Get each unique pair
          var pairs = households.Where(h => h.Cars.Count > 1).SelectMany(h =>
          {
              List<Tuple<string, string>> innerpairs = new List<Tuple<string, string>>();
              for (int i = 0; i < h.Cars.Count - 1; i++)
              {
                  for (int j = i + 1; j < h.Cars.Count; j++)
                  {
                      if (string.Compare(h.Cars[i], h.Cars[j]) < 0)
                      {
                          innerpairs.Add(new Tuple<string, string>(h.Cars[i], h.Cars[j]));
                      }
                      else
                      {
                          innerpairs.Add(new Tuple<string, string>(h.Cars[j], h.Cars[i]));
                      }
                  }
              }
              return innerpairs;
          }).Distinct().ToList();
      
          foreach (var p in pairs)
          {
              Console.WriteLine("Households with {0} and {1}: {2}", p.Item1, p.Item2, HouseholdsWith(households, p.Item1, p.Item2));
          }
      }
      

      产生如下输出:

      Households with BMW 760: 3  
      Households with only BMW 760: 2
      
      Households with Honda Civic: 3  
      Households with only Honda Civic: 1
      
      Households with Honda Odyssey: 2  
      Households with only Honda Odyssey: 0
      
      Households with Mercedes S: 1  
      Households with only Mercedes S: 0
      
      Households with Smart Car: 1  
      Households with only Smart Car: 0
      
      Households with Tesla X: 2  
      Households with only Tesla X: 0
      
      Households with Toyota Camry: 2  
      Households with only Toyota Camry: 0
      
      Households with Honda Civic and Toyota Camry: 2 
      Households with BMW 760 and Mercedes S: 1 
      Households with BMW 760 and Smart Car: 1 
      Households with Mercedes S and Smart Car: 1 
      Households with Honda Odyssey and Tesla X: 2
      

      【讨论】:

        猜你喜欢
        • 2021-01-21
        • 1970-01-01
        • 2017-05-15
        • 1970-01-01
        • 2014-02-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-07
        相关资源
        最近更新 更多