- •Тема 4. Формат даних xml. Основи.
- •4.1. Вступ у xml
- •4.3. Організація роботи із стандартом xml у .Net
- •4.3.1. Класи XmlTextReader і XmlTextWriter
- •4.3.2. Обробка xml у пам’яті
- •XmlDocument
- •Класи XmlNode та XmlLinkedNode
- •Клас XmlDeclaration
- •Класи XmlComment та XmlAttribute
- •Клас XmlElement
- •Клас XmlDocument
- •Приклад сторення, навігації та пошук даних
- •4.3.3. Трансформація xml
4.3. Організація роботи із стандартом xml у .Net
Платформа .NET Framework дозволяє маніпулювати даними XML за допомогою набору класів з простору імен System.Xml (та інших просторів імен, які починаються з System.Xml).
4.3.1. Класи XmlTextReader і XmlTextWriter
Самий простий спосіб читання і запису XML полягає в застосуванні двох потокових класів: XmlTextReader і XmlTextWriter. Ці класи слід використовувати насамперед при наявності величезних XML-файлів, які було б непрактично утримувати цілком в пам'яті як один документ. Вони також підходять для обробки найпростіших XML.
Запис файлів XML
Починаємо зі створення об'єкту XmlTextWriter з передачею конструктору фізичного шляху файлу, який повинен бути створений:
string xmlFile = Server.MapPath("StudentList.xml");
using(XmlTextWriter writer = new XmlTextWriter(xmlFile, null))
{
...
}
Другий параметр конструктора XML вказує кодування. Для використання стандартного кодування UTF-8 необхідно передати null-посилання.
XmlTextWriter має такі властивості, як Formatting і Indentation, які дозволяють вказати, чи повинні дані XML забезпечуватися автоматичними відступами, формуючи звичну ієрархічну структуру, і вказати кількість пробілів для відступів.
Ці дві властивості можна встановити таким чином:
writer.Formatting = Formatting.Indented;
writer.Indentation = 3;
Метод WriteStartDocument() записує рядок декларації XML з версією 1.0 (<? xml version="1.0">), як показано нижче:
writer.WriteStartDocument();
Метод WriteComment() записує коментар. Його можна використовувати для додавання коментаря, наприклад із часом та датою створення:
writer.WriteComment("Created @ " + DateTime.Now.ToString()) ;
Далі необхідно записати вміст - елементи, атрибути і т.д. У цьому прикладі будується XML-документ, який представляє список студентів, який включає таку інформацію, як ПІБ, номер залікової книжки, факультет, дата народження, група, середній бал.
Номер залікової книжки будемо записувати у якості атрибута, а всі інші - будуть дочірніми елементами батьківського елементу <StudentList>, який повинен бути створений першим:
writer.WriteStartElement("StudentList");
Тепер можна створювати дочірні вузли. Наступний код відкриває новий елемент <student>:
writer.WriteStartElement("student");
Далі код записує атрибут, який представляє номер залікової книжки (ID). Ця інформація додається до стартового дескриптору елементу <student>:
writer.WriteAttributeString("ID","1");
Наступний крок полягає в додаванні інформації про студента всередину елемента <student>. Ці елементи не повинні мати власних дочірніх елементів, тому їх можна записати і встановити їх значення більш ефективно - єдиним викликом методу WriteElementString(). Метод WriteElementString() приймає два аргументи: ім'я елемента і його значення (завжди у вигляді рядка), як показано нижче:
writer.WriteElementString("Name", "Петренко Петро Петрович");
writer.WriteElementString("Department", "Математичний");
writer.WriteElementString("BDate", "23.07.1995");
writer.WriteElementString("Group", "П21");
writer.WriteElementString("Rating", "67");
В цей момент код записав всю інформацію про поточного студента. Наступний крок полягає у закритті всіх відкритих дескрипторів в зворотному порядку. Для цього потрібно просто викликати метод WriteEndElement() по одному разу для кожного раніше відкритого елемента. При цьому вказувати ім'я елемента не знадобиться, бо при кожному виклику WriteEndElement() він буде автоматично записувати закриваючий дескриптор для останнього відкритого елемента.
writer.WriteEndElement(); // запише </student>
Тепер створимо ще один елемент <student>, використовуючи той же підхід:
writer.WriteStartElement("student");
writer.WriteAttributeString("ID", "2");
writer.WriteElementString("Name", "Іваненко Іван Іванович");
writer.WriteElementString("Department", "Фізичний");
writer.WriteElementString("BDate", "01.01.1995");
writer.WriteElementString("Group", "Ф22");
writer.WriteElementString("Rating", "90");
writer.WriteEndElement();
Для завершення документа необхідно просто закрити елемент <StudentList> ще одним викликом WriteEndElement():
writer.WriteEndElement();
Як результат отримаємо такий xml-файл:

Завжди непогано ідентифікувати свою XML-граматику, призначаючи їй унікальний простір імен XML, як було описано раніше в цьому розділі. Для цього служить перевантажена версія методів WriteStartElement() та WriteElementString(), які приймають URI і префікс простору імен:
string xmlFile = Server.MapPath("StudentList.xml");
string URI = "http://http://www.uzhnu.edu.ua/";
using (XmlTextWriter writer = new XmlTextWriter(xmlFile, null))
{
writer.Formatting = Formatting.Indented;
writer.Indentation = 3;
writer.WriteStartDocument(true);
writer.WriteComment("Created @ " + DateTime.Now.ToString());
writer.WriteStartElement("uzhnu", "StudentList", URI);
//1
writer.WriteStartElement("student",URI);
writer.WriteAttributeString("ID", "1");
writer.WriteElementString("Name", URI, "Петренко Петро Петрович");
writer.WriteElementString("Department", URI, "Математичний");
writer.WriteElementString("BDate", URI, "23.07.1995");
writer.WriteElementString("Group", URI, "П21");
writer.WriteElementString("Rating", URI, "67");
writer.WriteEndElement();
//2
writer.WriteStartElement("student",URI);
writer.WriteAttributeString("ID", "2");
writer.WriteElementString("Name", URI, "Іваненко Іван Іванович");
writer.WriteElementString("Department", URI, "Фізичний");
writer.WriteElementString("BDate", URI, "01.01.1995");
writer.WriteElementString("Group", URI, "Ф22");
writer.WriteElementString("Rating", URI, "90");
writer.WriteEndElement();
// </StudentList>
writer.WriteEndElement();
}
У цьому випадку, як результат отримаємо такий xml-файл:

Читання файлів XML
Хоча читання XML-файлу за допомогою об'єкта XmlTextReader являє собою найбільш простий підхід, з іншого боку цей підхід є найменш гнучким. Файл читається послідовно, і не можна вільно переміщатися до батьківського, дочірньому або сусіднього вузла (елемента), як це можна робити при обробці розташованого в пам'яті XML. Замість цього ви читаєте вузли по одному за раз з потоку. Зазвичай для цього організовується один або більше вкладених циклів, щоб проходити по елементам XML-документа доти, поки не буде прочитано всі вузли.
Наступний код починається із завантаження вихідного файлу в об'єкт XmlTextReader. Потім відкривається цикл, який проходить по документу, перебираючи вузли один за іншим. Для переходу від поточного вузла до наступного викликається метод XmlTextReader.Read(). Цей метод повертає true доти, поки не переміститься за останній вузол, після чого він поверне false. Це нагадує підхід, використовуваний класом DataReader для витягання результатів запиту із бази даних. Ось приклад коду для зчитування із xml-файлу, який був сформований нами вище.
string xmlFile = Server.MapPath("StudentList.xml");
using (XmlTextReader reader = new XmlTextReader(xmlFile))
{
StringBuilder str = new StringBuilder();
reader.ReadStartElement("StudentList");
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "student")
{
var id = reader.GetAttribute("ID");
reader.ReadStartElement("student");
str.AppendFormat("<h3>{0}</h3>", reader.ReadElementString("Name"));
str.AppendFormat(@"<ul><li>{0}</li><li>{1}</li><li>{2}</li><li>{3}</li><li>{4}</li></ul>",
id,
reader.ReadElementString("Department"),
reader.ReadElementString("BDate"),
reader.ReadElementString("Group"),
reader.ReadElementString("Rating"));
}
}
content.InnerHtml = str.ToString();
}
Як результат у браузері побачимо:

