Чтение данных JSON в группах - PullRequest
0 голосов
/ 24 мая 2019

У меня есть данные 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
    • с Civic и Camry: 2
    • , только с Civic равен 1 и т. Д.

Я пытался сделать то, что указано в этот вопрос , но это не сработало.

Ответы [ 3 ]

0 голосов
/ 24 мая 2019

Прежде всего используйте json2csharp или параметр PasteSpecial для создания модели для анализа json.

Параметр PasteSpecial вы можете найти в Правка -> Специальная вставка -> Вставить JSON как классы

enter image description here

Это даст вам правильную модель для анализа вашей строки 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
0 голосов
/ 24 мая 2019

Собственный тип данных для этого результата JSON: List<Dictionary<string, string[]>>.Вы можете проанализировать его напрямую с помощью:

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 в домохозяйства, вы можете сделать что-то вроде этого:

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
0 голосов
/ 24 мая 2019

Вы должны сначала сгладить вложенный список имен автомобилей, а затем сгруппировать их, чтобы получить желаемый результат.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...