Анализ XML-данных на С# и отображение в ListBox

Я пытаюсь проанализировать XML файл на С# с помощью Visual Studio и показать данные в ListBox, но я не знаю, как его разобрать, когда я имею дело с вложенным XML файлом.

Это код из файла XML:

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE root [
  <!ELEMENT root (Persons*)>
  <!ELEMENT Persons (name)>
  <!ELEMENT IsMale (#PCDATA)>
  <!ELEMENT Age (#PCDATA)>
  <!ELEMENT Name (#PCDATA)>
  <!ELEMENT LikedPerson (name)>
 ]>
<root>
  <Persons name ="Bob">
    <IsMale>true</IsMale>
    <Age>30</Age>
    <LikedPerson name ="Iulia">
      <IsMale>false</IsMale>
      <Age>32</Age>
    </LikedPerson>
  </Persons>
</root>

Код, который я написал на С#, успешно возвращает мне только имя, пол и возраст для каждого человека, но я не знаю, как писать, чтобы показать мне также person_liked:

private void LoadPersons()
    {
        XmlDocument doc = new XmlDocument();
        doc.Load("Baza_de_cunostinte.xml");

        foreach (XmlNode node in doc.DocumentElement) 
        {
            string name = node.Attributes[0].Value;
            int age = int.Parse(node["Age"].InnerText);
            bool isMale = bool.Parse(node["IsMale"].InnerText);

//          Persons likedPerson.name = Persons.node.Attributes[0].Value ?  
//          .....

            listBox.Items.Add(new Persons(name, age, isMale, likedPerson));
        }
    }

    private void listBox_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (listBox.SelectedIndex != -1)
        {
            propertyGrid1.SelectedObject = listBox.SelectedItem;
        }
    }

Это определение Person.cs:

class Persons
{
    public string Name { get; private set; }
    public int Age { get; private set; }
    public bool IsMale { get; private set; }
    public Persons LikedPerson { get; private set; }

    public Persons(string name, int age, bool isMale, Persons likedPerson)
    {
        Name = name;
        Age = age;
        IsMale = isMale;
        LikedPerson = likedPerson;
    }
}

Ответ 1

XmlSerializer mySerializer = new XmlSerializer(typeof(Persons));
// Create a FileStream or textreader to read the xml data.
FileStream myFileStream = new FileStream("xmldatafile.xml", FileMode.Open);

var person = (Persons)  mySerializer.Deserialize(myFileStream);

Вам также нужно добавить конструктор без параметра для класса Person.

Ответ 2

Самый естественный способ сделать это - использовать XmlSerializer, как было предложено, но для этого вам придется немного реорганизовать свои классы:

[XmlType(Namespace="", TypeName="root")]
public class PersonCollection
{
    [XmlElement(Namespace="", ElementName="Persons")]
    public List<Persons> People { get; set; }
}

public class Persons
{
    [XmlAttribute(AttributeName="name")]
    public string Name { get; set; }
    public int Age { get; set; }
    public bool IsMale { get; set; }

    public Persons LikedPerson { get; set; }

    public Persons() { }

    public Persons(string name, int age, bool isMale, Persons likedPerson)
    {
        Name = name;
        Age = age;
        IsMale = isMale;
        LikedPerson = likedPerson;
    }
}

Затем вы можете сделать что-то вроде этого:

XmlSerializer ser = new XmlSerializer(typeof(PersonCollection));

PersonCollection pc = (PersonCollection)ser.Deserialize(File.OpenRead("Baza_de_cunostinte.xml"));
foreach (Persons p in pc.People)
{
   // you now have a fully populated object
}

а список pc.People будет содержать ваши объекты Persons.

Ответ 3

@user3063909,

1- Используйте XSD для определения XML. Пример:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="root">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Persons" maxOccurs="unbounded" minOccurs="0" type="Persons"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:complexType name="Persons">
    <xs:sequence>
      <xs:element type="xs:string" name="IsMale"/>
      <xs:element type="xs:int" name="Age"/>
      <xs:element name="LikedPerson" type="Persons"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="name" />
  </xs:complexType>
</xs:schema>   

2- Класс Person должен выглядеть следующим образом:

namespace StackOverflow
{
    public class Root
    {
        [XmlElement("Persons")]
        public List<Persons> Persons { get; set; }
    }

    public class Persons
    {
        public string IsMale { get; set; }
        public int Age { get; set; }
        public Persons LikedPerson { get; set; }

        [XmlAttribute("Name")]
        public string Name { get; set; }
    }
}

3- Класс сериализатора:

namespace StackOverflow
{
    public class XmlSerializerHelper<T> where T : class 
    {
        private readonly XmlSerializer _serializer;

        public XmlSerializerHelper()
        {
            _serializer = new XmlSerializer(typeof(T));
        }

        public T BytesToObject(byte[] bytes)
        {
            using (var memoryStream = new MemoryStream(bytes))
            {
                using (var reader = new XmlTextReader(memoryStream))
                {
                    return (T)_serializer.Deserialize(reader);
                }
            }
        }
    }
}

4- И, наконец, назовите его так:

var fileBytes = File.ReadAllBytes("C:/xml.xml");
var persons = new XmlSerializerHelper<Root>().BytesToObject(fileBytes);

Результатом будет корневой класс со списком лиц.

Приветствия.

Ответ 4

Вы можете получить LikedPerson node и получить его имя/возраст, как сейчас. Во избежание дублирования кода вы можете создать метод, который принимает XmlNode, рекурсивно решает его и возвращает Person. Но лучший способ - использовать XmlSerializer

foreach (XmlNode node in doc.DocumentElement) 
{
    string name = node.Attributes[0].Value;
    int age = int.Parse(node["Age"].InnerText);
    bool isMale = bool.Parse(node["IsMale"].InnerText);

    var likedPerson = node.SelectSingleNode("LikedPerson");

    if (likedPerson != null){
        string name = likedPerson.Attributes[0].Value;
        //age, gender, etc.        
    }        
}