Как считать список файлов JSON по очереди? C#
Есть файлы в формате JSON. В их храниться информация о пользователях и их показателях за каждый день. Мне нужно эти показатели считать. Но проблема в том, что каждый день это отдельный файл. Как с этим быть?
вот класс:
class DataJson
{
public int Rank { get; set; }
public string User { get; set; }
public string Status { get; set; }
public int Steps { get; set; }
}
System.Net.WebClient wc = new System.Net.WebClient();
String response = Encoding.UTF8.GetString(wc.DownloadData($"http://slova.by/JSON_test/Files/day1.json"));
var obj = JsonConvert.DeserializeObject<DataJson[]>(response);
Ответы (3 шт):
Для случая локального хранения файлов.
using System.Linq;
DirectoryInfo info = new DirectoryInfo("PATH_TO_DIRECTORY_HERE");
FileInfo[] files = info.GetFiles().OrderBy(p => p.CreationTime).ToArray();
foreach (FileInfo file in files)
{
// DO Something...
}
если у Вас удаленный репозиторий то нужно сначала синхронизировать каталоги
(локальный, удаленный) и сделать как код выше но уже делать сортировку по имени .OrderBy(p => p.FileName) или как вам удобно
Обновлено. А вообще держите данные по клиенту также как и есть и тогда очерёдность будет определяться так как вы хотите.
для каждого файла
- считываете файл
- парсите данные
- для каждой записи в файле добавить/обновить информацию за считанный день (ключ) по клиенту в общий список.
AllUserData = { User1: { day1: DataJson, day2: DataJson, ... dayn: DataJson, }
, ......
UserK: { day2: DataJson, ... dayj: DataJson, } ..... }
Для начала предлагаю использовать HttpClient вместо WebClient, хотя-бы потому что здесь указано, что:
Не рекомендуется использовать класс
WebClientдля новой разработки. Вместо этого используйте классSystem.Net.Http.HttpClient.
Вторая причина - у HttpClient толковое современное асинхронное API.
- Создайте
HttpClientотдельно, потому что
HttpClientis intended to be instantiated once per application, rather than per-use.
private static readonly HttpClient client = new HttpClient();
- Реализуйте метод загрузки
private async Task<List<JsonData>> LoadJsonDataAsync(url)
{
using (HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false))
{
response.EnsureSuccessStatusCode();
string responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
return JsonConvert.DeserializeObject<List<JsonData>>(responseText);
}
}
- Грузите
private async Task LoadAllDataAsync()
{
Dictionary<string, List<JsonData>> data = new Dictionary<string, List<JsonData>>();
try
{
for (int i = 1; i < 31; i++);
{
string fileName = $"day{i}.json";
data.Add(fileName, await LoadJsonDataAsync($"http://slova.by/JSON_test/Files/{fileName}"));
}
}
catch (Exception ex)
{
// Обработайте здесь вероятное исключение.
}
foreach (KeyValuePair<string, List<JsonData>> pair in data)
{
// здесь можно что-то с данными сделать, pair.Key - имя файла, pair.Value - список
}
}
Подробнее про асинхронное программирование можно почитать здесь или здесь
Как объединить данные вам уже ответили, я сейчас покажу лишь как поступил бы я на вашем месте и почему.
Загрузка данных
Вам уже пояснили другим ответом, что WebClient использовать сейчас, для новых разработок плохо, научитесь пользоваться HttpClient. Также стоит учесть тот факт, что данные вам попросту не нужны на компьютере, а если и нужны, то сохраняйте наверно уже "слепок" того, что получили после объединения.
Давайте сделаем некий метод, который будет отдавать нам каждого человека, которого мы можем обработать так, как нам захочется. И тут нам на помощь приходят нововведения, а именно Асинхронные стримы, которые в C#8 позволяют получать данные по мере поступления, а не пачками.
Сделаем класс, который будет содержать в себе данные одного файла с сервера, вы его уже сделали, я просто переделал под нужды этого проекта, а также запретил менять данные из вне (ведь нам не надо, чтобы в момент разработки мы случайно начали менять данные, полученные с сервера, верно?)
class ServerData { public string User { get; } public int Steps { get; } public int Day { get; private set; } public void SetDay(int day) => Day = day; public ServerData(string user, int steps) => (User, Steps) = (user, steps); }Dayтут служит неким идентификатором дня, когда пользователь сделал те самые шаги, ибо как оказалось не все пользователи начинали отчет с самого начала. Также вы спросите, а где статус и ранг? Ну в статусе я не вижу смысла, ибо он всегда один, а ранг дам простой подсчет кто сделал больше шагов, а это мы можем сделать сами.Теперь сам метод получения, он будет примерно таким:
private readonly HttpClient httpClient = new HttpClient(); private async IAsyncEnumerable<ServerData> GetDataAsync() { int count = 1; while (true) { string data; try { data = await httpClient.GetStringAsync($"http://slova.by/JSON_test/Files/day{count}.json"); } catch (HttpRequestException e) when (e.Message.Contains("404")) { Debug.WriteLine("Файла нет на сервере"); break; } catch (Exception e) { Debug.WriteLine(e.Message); break; } var result = JsonConvert.DeserializeObject<ServerData[]>(data); foreach (var item in result) { item.SetDay(count); yield return item; } count++; } }Здесь по сути все просто, мы в бесконечном цикле берем данные до тех пор, пока сервер не выдаст ошибку (для примера я обработал 404 и все остальные, тут смотрите сами как поступить). В цикле мы пробуем получить ответ от сервера в виде
stringи если он успешный, то мы десериализуем это в массивServerData, который в последующем перебираем (задав нужную дату файла) и отдаем наружу. Ну и в конце счетчик прибавляем.
Разбираем данные
Имея код выше мы можем теперь составить коллекцию всех пользователей в удобном нам виде, который позволит удобно просматривать и сортировать данные.
Создадим класс одного человека. В него мы поместим ФИО, а также коллекцию пройденных шагов и в какой день. Каждый шаг пусть будет отдельным классом.
class DaySteps { public int Day { get; } public int Steps { get; } public DaySteps(int day, int steps) => (Day, Steps) = (day, steps); } class User { public string Name { get; } public List<DaySteps> Steps { get; } public User(string name) => (Name, Steps) = (name, new List<DaySteps>()); public bool HasADay(int day) => Steps.Any(x => x.Day == day); }HasADay- это предикат, который вернетtrue, если пользователь вел учет шагов в указанный день.Берем теперь данные с сервера и формируем коллекцию людей:
private List<User> Users { get; } = new List<User>(); public async Task<UsersManager> LoadDataAsync() { await foreach (var data in GetDataAsync()) { if (!Users.Any(x => x.Name == data.User)) { var user = new User(data.User); user.Steps.Add(new DaySteps(data.Day, data.Steps)); Users.Add(user); } else { var user = Users.FirstOrDefault(x => x.Name == data.User); user.Steps.Add(new DaySteps(data.Day, data.Steps)); } } return this; }Тут я думаю все предельно просто: есть человек - ищем, добавляем новый день с новыми шагами. Нету - добавляем. Единственное что может смутить, так это
return this, так вот, я это сделал для удобства, чтобы потом можно было использовать так называемый "цепочный вызов".
Все, теперь у нас есть коллекция с объединенными данными по всем пользователям (как вы и просили).
Работа с данными
Теперь мы сами хозяева данных и в праве сами с ними как угодно распоряжаться, пару примеров:
Сериализуем в один JSON:
public void ToJSON(string file) => File.WriteAllText(file, JsonConvert.SerializeObject(Users, Formatting.Indented));Выводи рейтинг за указанный день (прям как на сайте):
public UsersManager PrintTop(int day) { Console.WriteLine($"--- Рейтинг за {day} день ---"); var users = Users.Where(x => x.HasADay(day)) .Select(x => (x.Name, x.Steps.FirstOrDefault(s => s.Day == day).Steps)) .OrderByDescending(x => x.Steps) .ToList(); for (int i = 0; i < users.Count; i++) { var user = users[i]; Console.WriteLine($"[{i + 1}] {user.Name}: {user.Steps}"); } return this; }
Ну и так далее, примеров может быть много. Вызов этого всего будет тогда такой:
var manager = new UsersManager();
(await manager.LoadDataAsync()).PrintTop(15).ToJSON("Data.json");
Что даст нам файл JSON с полностью объединенными данными и выведет в консоль топ
Как видите, если взять чуть больше и сделать некий комбайн (я это вовсе делал на коленке, да криво), получится весьма годный источник данных с которым удобно будет работать, так что удачи в изучении C#!
P.S. Не гнался за решением конкретно поставленной задачи, хотел лишь показать как можно поступить.

