Чтение нескольких дочерних узлов XML файла

Я создал файл Xml с содержимым примера следующим образом:

<?xml version="1.0" encoding="utf-8" ?>
<Periods>
  <PeriodGroup name="HER">
    <Period>
      <PeriodName>Prehistoric</PeriodName>
      <StartDate>-500000</StartDate>
      <EndDate>43</EndDate>
    </Period>
    <Period>
      <PeriodName>Iron Age</PeriodName>
      <StartDate>-800</StartDate>
      <EndDate>43</EndDate>
    </Period>
    <Period>
      <PeriodName>Roman</PeriodName>
      <StartDate>43</StartDate>
      <EndDate>410</EndDate>
    </Period>
  </PeriodGroup>
  <PeriodGroup name="CAFG">
    <Period>
      <PeriodName>Prehistoric</PeriodName>
      <StartDate>-500000</StartDate>
      <EndDate>43</EndDate>
    </Period>
    <Period>
      <PeriodName>Roman</PeriodName>
      <StartDate>43</StartDate>
      <EndDate>410</EndDate>
    </Period>
    <Period>
      <PeriodName>Anglo-Saxon</PeriodName>
      <StartDate>410</StartDate>
      <EndDate>800</EndDate>
    </Period>   
  </PeriodGroup>
</Periods>

Мне нужно иметь возможность читать дочерние элементы Period node в пределах выбранной группы PeriodGroup. Я думаю, что PeriodName может быть атрибутом Периода, если это более разумно.

Я просмотрел множество примеров, но ни один из них не кажется вполне правильным, и, похоже, существуют десятки разных методов, некоторые из которых используют XmlReader, некоторые XmlTextReader и некоторые из них не используются. Поскольку это мой первый раз, когда я читал Xml файл, я думал, что спрошу, может ли кто-нибудь дать мне указатель. У меня есть что-то работающее, чтобы попробовать что-то, но оно неуклюже. Я использую VS2010 и С#. Кроме того, я вижу, что многие люди используют LINQ-Xml, поэтому я буду благодарен за плюсы и минусы использования этого метода.

string PG = "HER";
XmlDocument doc = new XmlDocument();
doc.Load(Server.MapPath("./Xml/XmlFile.xml"));
string text = string.Empty;
XmlNodeList xnl = doc.SelectNodes("/Periods/PeriodGroup");
foreach (XmlNode node in xnl)
{
    text = node.Attributes["name"].InnerText;
    if (text == PG)
    {
        XmlNodeList xnl2 = doc.SelectNodes("/Periods/PeriodGroup/Period");
        foreach (XmlNode node2 in xnl2)
        {
            text = text + "<br>" + node2["PeriodName"].InnerText;
            text = text + "<br>" + node2["StartDate"].InnerText;
            text = text + "<br>" + node2["EndDate"].InnerText;
        }
    }
    Response.Write(text);
}

Ответ 1

Вы можете использовать подход XPath так:

XmlNodeList xnl = doc.SelectNodes(string.Format("/Periods/PeriodGroup[@name='{0}']/Period", PG));

Хотя он предпочитает LINQ to XML для его читаемости.

Это приведет к возврату дочерних элементов Period node на основе атрибута PeriodGroup name, например. HER:

XDocument xml = XDocument.Load(HttpContext.Current.Server.MapPath(FileLoc));

var nodes = (from n in xml.Descendants("Periods")
            where n.Element("PeriodGroup").Attribute("name").Value == "HER"
            select n.Element("PeriodGroup").Descendants().Elements()).ToList();

Результаты:

<PeriodName>Prehistoric</PeriodName>
<StartDate>-500000</StartDate>
<EndDate>43</EndDate>

<PeriodName>Iron Age</PeriodName>
<StartDate>-800</StartDate>
<EndDate>43</EndDate>

<PeriodName>Roman</PeriodName>
<StartDate>43</StartDate>
<EndDate>410</EndDate>

Запрос довольно простой

from n in xml.Descendants("Periods")

Вернет коллекцию элементов-потомков для элемента Periods. Затем мы используем where для фильтрации этой коллекции узлов на основе значения атрибута:

where n.Element("PeriodGroup").Attribute("name").Value == "HER"

Затем отфильтруйте коллекцию до элементов PeriodGroup, у которых есть атрибут name со значением HER

Наконец, мы выбираем элемент PeriodGroup и получаем его узлы-потомки

select n.Element("PeriodGroup").Descendants().Elements()

ИЗМЕНИТЬ (см. комментарии)

Поскольку результат этого выражения является просто запросом, мы используем .ToList() для перечисления коллекции и возврата объекта, содержащего нужные вам значения. Вы также можете создавать анонимные типы для хранения значений элементов, например:

var nodes = (from n in xml.Descendants("Period").
             Where(r => r.Parent.Attribute("name").Value == "HER")
             select new
             {
                  PeriodName = (string)n.Element("PeriodName").Value,
                  StartDate = (string)n.Element("StartDate").Value,
                  EndDate = (string)n.Element("EndDate").Value
             }).ToList();

//Crude demonstration of how you can reference each specific element in the result
//I would recommend using a stringbuilder here..
foreach (var n in nodes)
{
      text += "<br>" + n.PeriodName;
      text += "<br>" + n.StartDate;
      text += "<br>" + n.EndDate;
}

Это то, что будет выглядеть объект nodes после выполнения запроса:

enter image description here

Ответ 2

Так как метод XmlDocument.SelectNodes фактически принимает выражение XPath, вы можете сделать следующее:

XmlNodeList xnl = doc.SelectNodes("/Periods/PeriodGroup[@name='" + PG + "']/Period");
foreach (XmlNode node in xnl) {
    // Every node here is a <Period> child of the relevant <PeriodGroup>.
}

Вы можете узнать больше о XPath в w3schools.

Ответ 3

перейти через этот

 public static void XMLNodeCheck(XmlNode xmlNode)
    {
        if (xmlNode.HasChildNodes)
        {
            foreach (XmlNode node in xmlNode)
            {
                if (node.HasChildNodes)
                {
                    Console.WriteLine(node.Name);
                    if (node.Attributes.Count!=0)
                    {
                        foreach (XmlAttribute att in node.Attributes)
                        {
                            Console.WriteLine("----------" + att.Name + "----------" + att.Value);
                        }
                    }

                    XMLNodeCheck(node);//recursive function 
                }
                else
                {
                    if (!node.Equals(XmlNodeType.Element))
                    {
                        Console.WriteLine(node.InnerText);
                    }

                }
            }
        }
    }