**
[Конспект "Форматы файлов"](https://github.com/kolei/OAP/blob/master/articles/t5_file_types.md)
CSV
** **CSV** (от англ. Comma-Separated Values — значения, разделённые запятыми) — текстовый формат, предназначенный для представления табличных данных. Строка таблицы соответствует строке текста, которая содержит одно или несколько полей, разделенных запятыми. **Спецификация
** * Каждая строка файла — это одна строка таблицы. * Разделителем (англ. delimiter) значений колонок является символ запятой (,). Однако на практике часто используются другие разделители, то есть формат путают с DSVruen и TSV (см. ниже). * Значения, содержащие зарезервированные символы (двойная кавычка, запятая, точка с запятой, новая строка) обрамляются двойными кавычками ("). Если в значении встречаются кавычки — они представляются в файле в виде двух кавычек подряд. **Чтение данных из существующего файла
** ```js using System.Globalization; using CsvHelper; using CsvHelper.Configuration; using (var reader = new StreamReader("./test.csv")) { using (var csv = new CsvReader( reader, CultureInfo.InvariantCulture)) { var records = csv.GetRecordsВывод:
** ```js строка с пробелами, 123,15 строка с переносом строки, 456 ``` * Сначала считывается содержимое файла обычным StreamReader (путь ./ означает, что чтение из текущего каталога) * Затем содержимое отдаётся классу CsvReader * При чтении данных (GetRecords) используются класс Foo (просто как описание структуры). Классы мы пока не проходили, но тут пока ничего сложного * В классе у нас определены поля, соответствующие колонкам в нашем файле * В цикле перебираем записи (строки) нашего файла и выводим содержимое в консоль. **Если у нас данные без заголовков или с другими разделителями, то можно задать конфигурацию:
** ```js ... var config = new CsvConfiguration( CultureInfo.InvariantCulture) { Delimiter = ";", HasHeaderRecord = false }; using (var reader = new StreamReader("./test2.csv")) { using (var csv = new CsvReader(reader, config)) ... ``` **Запись в CSV
** ```js // использую не стандартный формат var config = new CsvConfiguration( CultureInfo.InvariantCulture) { Delimiter = ";", HasHeaderRecord = false }; // создаю тестовые данные var records = new ListВывод:
** ```js "тест записи многострочного текста";12.34 просто, текст, с запятыми;0 ``` **Различия в культурной среде
** В русской культурной среде в качестве разделителя разрядов в числах с плавающей запятой используется запятая. Поэтому вполне может попасться csv файл, в котором в числах разделитель запятая. ```js "строка с пробелами";123,15 "строка с переносом строки";456 ``` Разделитель полей мы менять умеем, но как быть с форматом чисел? Нужно задать нужную культурную среду: ```js var config = new CsvConfiguration( new CultureInfo("ru-RU")) { Delimiter = ";", HasHeaderRecord = false }; ``` **JSON
** JSON (JavaScript Object Notation). Можно перевести как способ записи объектов в JavaScript. Формат оказался настолько удобен, что его стали поддерживать практически все популярные языки программирования. **Как устроен этот формат
** Допустим, у нас есть магазин с системой бонусов, которые начисляются по скидочной карте. Когда продавец считывает карту, он должен получить от сервера такие данные: * имя, * фамилию, * телефон, * город, * возраст, * количество бонусных баллов, * три предыдущие покупки (чтобы порекомендовать к ним что-то подходящее). А теперь посмотрите на JSON-ответ, который получит продавец после считывания карты: ```js { "firstname": "Михаил", "lastname": "Максимов", "phone": "+79201234567", "city": "Москва", "age": 37, "bonus": 2000, "prev": [ "Кроссовки", "Турник", "Зимняя куртка" ] } ``` Общее правило такое: сначала всегда идёт название какого-то поля, а через двоеточие — его значение. Названия всегда берутся в двойные кавычки, строковые значения — тоже. Ещё есть такое: * вложенные объекты берутся в фигурные скобки; * массивы берутся в прямоугольные скобки; * после каждой пары «свойство: значение» должна стоять запятая (в самом конце — не ставится). Так как JSON — универсальный формат передачи данных, то он может работать только с теми данными, которые есть в большинстве языков: * строки — тоже, как и названия, берутся в двойные кавычки; числа, можно дробные; * логические значения true или false; * массивы или объекты. То, что не входит в этот список, JSON не обработает и не выдаст сообщение об ошибке, потому что JSON — это просто формат данных и за его правильностью должен следить программист. **Newtonsoft.Json
** Эта библиотека является стандартом де-факто для работы с JSON в C#. Пример сериализации (преобразование объекта в JSON-строку) ```js var person = new Person { name = "Имя", age = 18, date = "2024-03-07" }; string json = JsonConvert.SerializeObject( person, Formatting.Indented); Console.WriteLine(json); ``` Должны получить что-то подобное: ```js { "name": "Имя", "age":18, "date":"2024-03-07" } ``` **Работа с JSON. DataContractJsonSerializer.
** **Сериализация
** ```js var PersonList = new ListВывод:
** ```js [{"age":25,"date":"\/Date(1609448400000+0300)\/","name":"Иванов"},{"age":35,"date":"\/Date(1609534800000+0300)\/","name":"Петров"}] ``` Замечания по коду: Первым параметром метод `Serializer.WriteObject` ждет поток данных (Stream). Передавать ему переменную streamWriter нельзя, т.к. у неё другой тип (StreamWriter). В инете почему-то для получения потока из файла используют многоступенчатую схему: сначала читают содержимое файла в объект типа MemoryStream затем передают этот объект в сериализатор Но при первом же вызове `InteliSense` видно, что у `StreamWriter-а` есть публичное свойство BaseStream, которое вполне можно использовать в сериализаторе. ```js var PersonList = (Person[])Serializer.ReadObject( new MemoryStream( Encoding.UTF8.GetBytes( "[{\"name\":\"Иванов\",\"age\":20},{\"name\":\"Петров\",\"age\":20}]" ) ) ); ``` **Десериализация
** Десериализовать будем такой файл, в нём используется наиболее распространенный формат даты: ```js [ {"age":25,"name":"Иванов","date":"2021-03-21"}, {"age":35,"name":"Петров","date":"2021-03-22"} ] ``` C# не поддерживает перегрузку свойств, поэтому мы не можем определить одноименные свойства с разными типами. Решение есть в атрибутах контракта и в том, что сериализатор "видит" и приватные свойства класса: * [IgnoreDataMember] - этот атрибут скрывает отмеченное свойство от сериализатора * [DataMember(Name = "альтернативное название")] - можно задать альтернативное имя для сериализуемого свойства. Перепишем класс Person с учетом этих атрибутов: ```js [DataContract] internal class Person { // создаем приватную переменную для хранения даты private DateTime privateDate; [DataMember] public string name { get; set; } [DataMember] public string age { get; set; } // создаем ПРИВАТНОЕ СТРОКОВОЕ свойство и с помощью атрибутов меняем ему имя для сериализатора [DataMember(Name = "date")] private string StringDate { get { return privateDate.ToString("yyyy-MM-dd"); } set { // 2021-03-21 // 0123456789 privateDate = new DateTime( Convert.ToInt32(value.Substring(0, 4)), Convert.ToInt32(value.Substring(5, 2)), Convert.ToInt32(value.Substring(8, 2)) ); } } // публичное свойство "дата" скрываем от сериализатора [IgnoreDataMember] public DateTime date { get { return privateDate; } set { privateDate = value; } } } ``` Таким образом при десериализации при задании свойства "дата" будет вызван сеттер свойства StringDate. А при использовании объекта Person в коде его публичное свойство date. Десериализация делается методом ReadObject, который на входе принимает поток с JSON-строкой. ```js var Serializer = new DataContractJsonSerializer(typeof(Person[])); using(var sr = new StreamReader("test.json")) { var PersonList = (Person[])Serializer.ReadObject(sr.BaseStream); ... ``` **Вариант попроще
** Можно использовать не только "голый" .NET Framework, но и библиотеки из других компонентов Visual Studio. В пространстве имён System.Web.Script.Serialization есть класс JavaScriptSerializer, который выглядит попроще чем классическая реализация: В пакет разработки C# не входит библиотека System.Web.Extensions (в которой и находится System.Web.Script.Serialization). Нужно в "Обозревателе решений" добавить в "Ссылки" библиотеку Сборки -> Платформа -> System.Web.Extensions ```js // целевые классы нам по прежнему нужны, но уже без всяких аннотаций internal class MaterialTC { public string Title { get; set; } public int Count { get; set; } } internal class Notice { public Material[] data; } internal class Answer { public Notice notice; } // в месте, где нам нужно распарсить JSON создаем сериализатор и разбираем строку var serializer = new JavaScriptSerializer(); var answer = serializer.Deserialize