Как просто распарсить XML в C#

Как просто распарсить XML в C#
Перед многими часто встаёт задача распарсить XML с помощью C#. В данной статье опишу простейшие методы разбора XML без излишней сложности и без подключения библиотек сторонних разработчиков. Описанных примеров хватит для большинства существующих задач.

Создадим в Visual Studio проект Windows Form и набросаем кнопки и текстовые поля и получим примерно такую форму:

Как просто распарсить XML в C#

По кнопке «Машина» мы будем получать из XML марку, номер и регион, в котором зарегистрирован автомобиль. По кнопке «Страна 2» мы будем получать из XML название второй страны и выводить данные в поле результата. По кнопке «Параметры Страна 1» мы будем получать из XML параметры первой страны (валюта, язык, столица) и выводить данные в поле результата.

Подключим к проекту:

using System;
using System.IO;
using System.Xml;
using System.Windows.Forms;

Объявим пару переменных:

/// <summary> Путь к рабочему каталогу, относительно исполняемого файла </summary>
public static string dirWork = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
/// <summary> Файл, который необходимо парсить </summary>
public static string fileXML = dirWork + @"\XMLFileForParse.xml";

«XMLFileForParse.xml» — это XML-файл который мы будем разбирать, и который имеет следующую структуру:

<?xml version="1.0" encoding="utf-8" ?>
<body>
  <ns2:TransportResponse xmlns:ns2="http://webservices.victorz.ru/">
    <TransportName>Газель</TransportName>
    <TransportNumber>р999вд</TransportNumber>
    <TransportRegion>77</TransportRegion>
  </ns2:TransportResponse>
  <country>
    <name>Россия</name>
    <param coin="рубль" language="русский" stolitsa="Москва" />
  </country>
  <country>
    <name>USA</name>
    <param coin="usd" language="english" stolitsa="New York" />
  </country>
</body>

Давайте получим из XML марку автомобиля, номер и регион. Все три значения мы будем получать разными способами, чтобы охватить шире тему. В коде все строки стараюсь комментировать подробно:

/// <summary>
/// Кнопка "Машина"
/// </summary>
private void button2_Click(object sender, EventArgs e)
{
	textBoxResult.Clear(); // очистить поле результатов
	XmlDocument docXML = new XmlDocument(); // XML-документ
	docXML.Load(fileXML); // загрузить XML
	string _name = docXML.GetElementsByTagName("TransportName")[0].InnerText; // получаем значение тега "TransportName", так как тег "TransportName" единственный, то обращаемся к нему напрямую (указывая порядковый номер/индекс [0])

	/* Далее попробуем получить данные поработав с пространством имён (в будущем может пригодиться)
	 * В тегах у нас имеется пространство имён "ns2" (ns2:TransportResponse)
	 * Адрес пространства имён искать в XML. Пишем код и читаем XML
	*/
	XmlNamespaceManager _namespaceManager = new XmlNamespaceManager(docXML.NameTable); // инициализация пространства имён
	_namespaceManager.AddNamespace("ns2", "http://webservices.victorz.ru/"); // указываем значение пространства имён "ns2" (данные взяли из XML)
	// вычислим регион отталкиваясь от родителя и при этом не с самого начала документа ведём поиск, индекс [0] обязателен
	string _region = docXML.DocumentElement.SelectNodes("//ns2:TransportResponse/TransportRegion", _namespaceManager)[0].InnerText; // получаем значение тега "TransportRegion"

	/* Определим номер машины по полному пути, т.е. перечисляя все теги с самого начала документа
	 * Обратите внимание, что используя пример ниже можно не объявлять пространство имён, если оно присутствует, а просто указываем тег вместе с пространством
	 */
	string _number = docXML["body"]["ns2:TransportResponse"]["TransportNumber"].InnerText; ; // получаем значение тега "TransportNumber"

	textBoxResult.Text = _name + " - " + _number + " - " + _region; // выводим результат: Газель - р999вд - 77
}

У нас в XML есть одинаковые теги «country», «name», «param».
Давайте получим название второй страны, тег «name»:

/// <summary>
/// Кнопка "Страна 2"
/// </summary>
private void button3_Click(object sender, EventArgs e)
{
	textBoxResult.Clear(); // очистить поле результатов
	/* У нас несколько тегов со странами нам нужно получить название второй страны это можно сделать несколькими способами */
	XmlDocument docXML = new XmlDocument(); // XML-документ
	docXML.Load(fileXML); // загрузить XML

        // выводим результат: USA
	textBoxResult.Text = docXML.GetElementsByTagName("name")[1].InnerText; // ищем сразу тег "name" с названием страны и получаем значение (указывая порядковый номер/индекс [1])
	// или
	textBoxResult.Text = docXML.DocumentElement.SelectNodes("//country/name")[1].InnerText; // ищем в теге "country" тег "name" с названием страны и получаем значение (указывая порядковый номер/индекс [1])
}

До этого мы получали значения тегов, однако у тегов есть атрибуты. Далее рассмотрим получение значения атрибутов у тега «param». Давайте получим значения атрибутов тега «param» у первой страны:

/// <summary>
/// Кнопка "Параметры Страна 1"
/// </summary>
private void button4_Click(object sender, EventArgs e)
{
	textBoxResult.Clear(); // очистить поле результатов
	/* Получим все значения атрибутов тега "param" у первой страны */
	XmlDocument docXML = new XmlDocument(); // XML-документ
	docXML.Load(fileXML); // загрузить XM
	string _coin = docXML.DocumentElement.SelectNodes("//country/param")[0].Attributes["coin"].Value; // получаем значение атрибута "coin"
	string _language = docXML.DocumentElement.SelectNodes("//country/param")[0].Attributes["language"].Value; // получаем значение атрибута "language"
	string _stolitsa = docXML.DocumentElement.SelectNodes("//country/param")[0].Attributes["stolitsa"].Value; // получаем значение атрибута "stolitsa"

	textBoxResult.Text = _coin + " - " + _language + " - " + _stolitsa; // выводим результат: рубль - русский - Москва
}

Основы рассмотрели. Надеюсь в работе вам это поможет. Для наглядности приложил пример проекта с расширенными комментариями.

10 комментариев

  1. Отличная инфа бро! Пили есчо

  2. Отличная статья!

  3. Спасибо за статью! Мне нужно распарсить служебный файл, там сложная структура, во многом Ваша статья мне помогла. Но вот в одном месте не могу сообразить, как правильно написать. В общем, структура xml допустим такая (упростила)

    <documents>
    <docdetails>
    <kind>01</kind>
    <number>08-03</number>
    <date>2019-05-03</date>
    </docdetails>
    <docdetails>
    <kind>02</kind>
    <number>843</number>
    <date>2019-05-15</date>
    </docdetails>
    <docdetails>
    <kind>03</kind>
    <number>LV007</number>
    <date>2019-05-15</date>
    </docdetails>
    </documents>

    Т.е. элемент с тегом docdetails встречается в файле несколько раз, но у него разное содержимое. Подскажите, как можно добавить условие, что:

    if (элемент с тегом docdetails имеет потомка kind  с тегом 01) {
    string text = значение тега number вложенного в этот docdetails (т.е. в данном случае это значение "08-03"
    }
    

    Спасибо заранее!

    • На скорую такой ответ:
      1. Соберите все «kind» и запишите в массив или в список.
      2. Определите в цикле какой из них содержит 01. Потом определите его порядковый номер в массиве.
      3.1. Если количество тегов «kind» равно количеству тегов «docdetails», то мы сразу узнаем порядковый номер «docdetails» и считаем вложенный в него «number».
      3.2. Или соберите все «number» в массив и порядковый номер «number» будет равен порядковому номеру «kind», его мы и считаем.

      Если подумать подольше, то можно и ещё варианты подобрать.

      • ну и задали вы мне задачку, учитывая то, что я чуть ли ни вчера занялась программированием ))) По первому варианту есть вопрос. Допустим я создаю List
        и мне нужно добавить значение всех тегов kind в него, но их может быть неограниченное (заранее неизвестное) количество. Вот как мне определить длину массива? Т.е. в данном файле их 3, и, если бы это было бы постоянная величина, то я бы написала так:

        List array = new List();
                for (int i = 0; i < 3; i++) 
                {
                    string _king = docXML.GetElementsByTagName("kind")[i].InnerText;
                    array.Add(_king);
                }
        • У List не надо задавать размер и это поможет, а по поводу реализации надо думать.
          Первое что приходит в голову это получить количество «kind» так:

          int count = xml.Elements("docdetails").Elements("kind").Count();
          • не знаю, не получилось. По первому варианту вроде все срослось с одним файлом, но загрузила другой, а там «kind» вложен также в другой элемент (не только в «docdetails»)

            • Тогда вам надо определиться какие «kind» вам нужны. Если все, то Count получайте по всем. Если определённые, то Count получайте по примеру выше.

  4. А для Json Есть пример работы??

    • Захаров Виктор

      Сейчас под рукой нет готового решения.
      Используйте пакет «Newtonsoft.Json».
      Как-нибудь на досуге напишу статью на эту тему.

Добавить комментарий

Ваш адрес email не будет опубликован.