|
|||||||
Работаем с LINQ to XML
Время создания: 09.11.2019 20:29
Текстовые метки: LINQ, XML, XDocument, XDocument, XElement, new XAttribute
Раздел: Компьютер - C# - XML
Запись: Kozlov-AE/Tetra/master/base/1573320591xfnrrss8jw/text.html на raw.githubusercontent.com
|
|||||||
|
|||||||
Создадим базу для ведения каталога аудиозаписей. База будет состоять из треков:
Мы научимся добавлять, редактировать, удалять и делать различные выборки из нашей базы. Для начала создадим консольное приложение (я пишу свои проекты на C#, но суть в общем-то понятна будет всем) и подключим необходимое пространство имен using System.Xml.Linq; Создание файлов XML Создадим XML файл нашей базы содержащий несколько тестовых записей уже при помощи LINQ: //задаем путь к нашему рабочему файлу XML string fileName = "base.xml"; //счетчик для номера композиции int trackId = 1; //Создание вложенными конструкторами. XDocument doc = new XDocument( new XElement("library", new XElement("track", new XAttribute("id", trackId++), new XAttribute("genre", "Rap"), new XAttribute("time", "3:24"), new XElement("name", "Who We Be RMX (feat. 2Pac)"), new XElement("artist", "DMX"), new XElement("album", "The Dogz Mixtape: Who's Next?!")), new XElement("track", new XAttribute("id", trackId++), new XAttribute("genre", "Rap"), new XAttribute("time", "5:06"), new XElement("name", "Angel (ft. Regina Bell)"), new XElement("artist", "DMX"), new XElement("album", "...And Then There Was X")), new XElement("track", new XAttribute("id", trackId++), new XAttribute("genre", "Break Beat"), new XAttribute("time", "6:16"), new XElement("name", "Dreaming Your Dreams"), new XElement("artist", "Hybrid"), new XElement("album", "Wide Angle")), new XElement("track", new XAttribute("id", trackId++), new XAttribute("genre", "Break Beat"), new XAttribute("time", "9:38"), new XElement("name", "Finished Symphony"), new XElement("artist", "Hybrid"), new XElement("album", "Wide Angle")))); //сохраняем наш документ doc.Save(fileName); Теперь в папке с нашей программой после запуска появится XML файл следующего содержания: <?xml version="1.0" encoding="utf-8"?> <library> <track id="1" genre="Rap" time="3:24"> <name>Who We Be RMX (feat. 2Pac)</name> <artist>DMX</artist> <album>The Dogz Mixtape: Who's Next?!</album> </track> <track id="2" genre="Rap" time="5:06"> <name>Angel (ft. Regina Bell)</name> <artist>DMX</artist> <album>...And Then There Was X</album> </track> <track id="3" genre="Break Beat" time="6:16"> <name>Dreaming Your Dreams</name> <artist>Hybrid</artist> <album>Wide Angle</album> </track> <track id="4" genre="Break Beat" time="9:38"> <name>Finished Symphony</name> <artist>Hybrid</artist> <album>Wide Angle</album> </track> </library> Для создания подобного файла средствами XmlDocument кода понадобилось где-то раза в 2 больше. В коде выше мы воспользовались конструктором класса XDocument, который принимает в качестве параметра перечень дочерних элементов, которыми мы изначально хотим инициализировать документ. Используемый конструктор XElement принимает в качестве параметра имя элемента, который мы создаем, а так же перечень инициализирующих элементов. Удобно то, что мы в этих элементах можем задавать как новые XElement, так и XAttribute. Последние отрендретятся в наш файл как атрибуты самостоятельно. Если вам не нравится использоваться такую вложенность конструкторов и вы считаете такой код громоздким, то можно переписать в более традиционный вариант. Код ниже даст на выходе аналогичный XML файл: XDocument doc = new XDocument(); XElement library = new XElement("library"); doc.Add(library); //создаем элемент "track" XElement track = new XElement("track"); //добавляем необходимые атрибуты track.Add(new XAttribute("id", 1)); track.Add(new XAttribute("genre", "Rap")); track.Add(new XAttribute("time", "3:24")); //создаем элемент "name" XElement name = new XElement("name"); name.Value = "Who We Be RMX (feat. 2Pac)"; track.Add(name); //создаем элемент "artist" XElement artist = new XElement("artist"); artist.Value = "DMX"; track.Add(artist); //Для разнообразия распарсим элемент "album" string albumData = "<album>The Dogz Mixtape: Who's Next?!</album>"; XElement album = XElement.Parse(albumData); track.Add(album); doc.Root.Add(track); /* *остальные элементы добавляем по аналогии */ //сохраняем наш документ doc.Save(fileName); Естественно выбирать необходимый способ нужно по ситуации. Чтение данных из файла Сейчас попробуем просто прочитать данные из уже полученного файла и вывести их в удобном для восприятия виде в консоль: //задаем путь к нашему рабочему файлу XML string fileName = "base.xml"; //читаем данные из файла XDocument doc = XDocument.Load(fileName); //проходим по каждому элементу в найшей library //(этот элемент сразу доступен через свойство doc.Root) foreach (XElement el in doc.Root.Elements()) { //Выводим имя элемента и значение аттрибута id Console.WriteLine("{0} {1}", el.Name, el.Attribute("id").Value); Console.WriteLine(" Attributes:"); //выводим в цикле все аттрибуты, заодно смотрим как они себя преобразуют в строку foreach (XAttribute attr in el.Attributes()) Console.WriteLine(" {0}", attr); Console.WriteLine(" Elements:"); //выводим в цикле названия всех дочерних элементов и их значения foreach (XElement element in el.Elements()) Console.WriteLine(" {0}: {1}", element.Name, element.Value); } Здесь в коде, думаю, ничего сложного нету и приведены комментарии. После запуска нашей программый в консоли отобразится следующий результат: track 1 Attributes: id="1" genre="Rap" time="3:24" Elements: name: Who We Be RMX (feat. 2Pac) artist: DMX album: The Dogz Mixtape: Who's Next?! track 2 Attributes: id="2" genre="Rap" time="5:06" Elements: name: Angel (ft. Regina Bell) artist: DMX album: ...And Then There Was X track 3 Attributes: id="3" genre="Break Beat" time="6:16" Elements: name: Dreaming Your Dreams artist: Hybrid album: Wide Angle track 4 Attributes: id="4" genre="Break Beat" time="9:38" Elements: name: Finished Symphony artist: Hybrid album: Wide Angle Изменение данных Попробуем пройтись по всем узлам library и увеличить аттрибут Id элемента track на 1. (дальше писать объявление пути к файлу и результат вывода в консоль я приводить не буду, чтобы не перегружать лишней информацией статью, все компилировал, все работает:) ): //Получаем первый дочерний узел из library XNode node = doc.Root.FirstNode; while (node != null) { //проверяем, что текущий узел - это элемент if (node.NodeType == System.Xml.XmlNodeType.Element) { XElement el = (XElement)node; //получаем значение аттрибута id и преобразуем его в Int32 int id = Int32.Parse(el.Attribute("id").Value); //увеличиваем счетчик на единицу и присваиваем значение обратно id++; el.Attribute("id").Value = id.ToString(); } //переходим к следующему узлу node = node.NextNode; } doc.Save(fileName); Теперь попробуем это сделать более правильным способом для наших задач: foreach (XElement el in doc.Root.Elements("track")) { int id = Int32.Parse(el.Attribute("id").Value); el.SetAttributeValue("id", --id); } doc.Save(fileName); Как видим – этот способ нам подошел больше. Добавление новой записи Добавим новый трек в нашу библиотеку, а заодно вычислим средствами LINQ следующий уникальный Id для трека: int maxId = doc.Root.Elements("track").Max(t => Int32.Parse(t.Attribute("id").Value)); XElement track = new XElement("track", new XAttribute("id", ++maxId), new XAttribute("genre", "Break Beat"), new XAttribute("time", "5:35"), new XElement("name", "Higher Than A Skyscraper"), new XElement("artist", "Hybrid"), new XElement("album", "Morning Sci-Fi")); doc.Root.Add(track); doc.Save(fileName); Вот таким подним запросом ко всем элементам вычисляется максимальное значение аттрибута id у треков. При добавлении полученное максимальное значение инкрементируем. Само же добавление элемента сводится к вызову метода Add. Обратите внимание, что добавляем элементы в Root, так как иначе нарушим структуру XML документа, объявив там 2 корневых элемента. Так же не забывайте сохранять ваш документ на диск, так как до момента сохранения никакие изменения в нашем XDocument не отразятся в XML файле. Удаление элементов Попробуем удалить все элементы исполнителя DMX: IEnumerable<XElement> tracks = doc.Root.Descendants("track").Where( t => t.Element("artist").Value == "DMX").ToList(); foreach (XElement t in tracks) t.Remove(); В этом примере мы вначале выбрали все треки у который дочерний элемент artst удовлетворяет критерии, а потом в цикле удалили эти элементы. Важен вызов в конце выборки ToList(). Этим самым мы фиксируем в отдельном участке памяти все элементы, которые хотим удалить. Если же мы надумаем удалять из набора записей, по которому проходим непосредственно в цикле, мы получим удаление первого элемента и последующий NullReferenceException. Так что важно помнить об этом. По совету xaoccps удалять можно и более простым способом: IEnumerable<XElement> tracks = doc.Root.Descendants("track").Where( t => t.Element("artist").Value == "DMX"); tracks.Remove(); В этом случае приводить к списку наш полученный результат вызовом функции ToList() не нужно. Почему этот способ не использовал изначально описал в комментарии :) Немного дополнительных запросов к нашей базе треков Отсортируем треки по продолжительности в обратном порядке: IEnumerable<XElement> tracks = from t in doc.Root.Elements("track") let time = DateTime.Parse(t.Attribute("time").Value) orderby time descending select t; foreach (XElement t in tracks) Console.WriteLine("{0} - {1}", t.Attribute("time").Value, t.Element("name").Value); Отсортируем элементы по жанру, исполнителю, названию альбома, названию трека: IEnumerable<XElement> tracks = from t in doc.Root.Elements("track") orderby t.Attribute("genre").Value, t.Element("artist").Value, t.Element("album").Value, t.Element("name").Value select t; foreach (XElement t in tracks) { Console.WriteLine("{0} - {1} - {2} - {3}", t.Attribute("genre").Value, t.Element("artist").Value, t.Element("album").Value, t.Element("name").Value); } Простенький запрос, выводящий количество треков в каждом альбоме: var albumGroups = doc.Root.Elements("track").GroupBy(t => t.Element("album").Value); foreach (IGrouping<string, XElement> a in albumGroups) Console.WriteLine("{0} - {1}", a.Key, a.Count()); Выводы После того как вы освоили пространство имен System.Xml для работы с XML на более низком уровне, смело переходите на использование System.Xml.Linq, надеюсь, написанная статья поможет это сделать быстрее, ведь не так страшен черт, как его рисуют. Как видно из примеров выше — многие вещи делать значительно проще, количество строк кода сокращается. Это дает нам очевидные преимущется, начиная со скорости разработки, заканчивая более легким сопровождением кода, написанного ранее. |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|