none
Поиск элеметов XML и их вывод. RRS feed

  • Вопрос

  • Всем привет.

    Есть XML, который имеет простую структуру:

    <Employee>  
      <people>    
       <Name>Василий</Name>   
       <LastName>Петрович</LastName> 
      </people>
    </Employee>

    И так далее с десяток записей.

    Есть форма WPF на которую помещён ListBox. В ListBox при загрузке формы помещаются имена сотрудников, взятые из <Name>. Как можно реализовать задачу, что если в ListBox нажимают на имя, то, с кажем с соседний Label будет выводится его фамилия?

    //Загрузка элементов Name и помещение их в ListBox
    XElement xElements = XElement.Load(@"Employee.xml");
                
                foreach (XElement xEle in xElements.Descendants("Name"))
                {
                    Listbox_Employee.Items.Add((string)xEle);
                }

    Да данный момент, у меня эта задача работает ровно на половину. То есть, если я запушу программу, добавлю новых пользователей, а потом в ListBox выберу новое имя, то его фамилия будет показана. Но, она будет показана ровно до перезапуска программы, потому что значения хранятся в оперативке во время выполнения.

    Я понимаю, что теоретически, после щелчка на объект ListBox, должен выполнится метод, который откроет XML и соотнесёт, что вот именно эта фамилия соотвествует именно этому объекту в ListBox.

    Есть идеи?

    Кстати, я работаю с помощью XElement.

    7 февраля 2017 г. 9:50

Ответы

  • вот это неправильно:

    XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));
    Employee DE = (Employee)serializer.Deserialize(reader);
    надо
    XmlSerializer serializer = new XmlSerializer(typeof(Employee));
    Employee DE = (Employee)serializer.Deserialize(reader);

    • Помечено в качестве ответа DenisAndreevich 9 февраля 2017 г. 11:09
    9 февраля 2017 г. 9:22

Все ответы

  • Добрый день.

    Если файл не огромный, то самый простой вариант, реализовать десериализацию этого файла в нормальные объекты в памяти. В процессе работать с нормальной коллекцией нормальных объектов. При завершении работы приложения сериализовать эту коллекцию в XML файл. А каждый раз перечитывать XML файл это не самый оптимальный с точки зрения производительности вариант.

    Если файл огромный, то можно как у вас выгрузить один из элементов и при SelectionChanged в ListBox перечитывать данные из файла, но тут есть две проблемы: перечитывание огромного файла будет идти очень медленно, если поле которое у вас показывает в ListBox не уникально для каждой записи, то вы будете находить первую запись с таким Name, а не нужную вам...

    • Предложено в качестве ответа Liliya Muray 7 февраля 2017 г. 14:09
    7 февраля 2017 г. 13:52
    Отвечающий
  • Решая задачу, я наткнулся на другую проблему. Я создал ещё более простую по структуре XML'ку:

    <MainData>
        <Name>Василий</Name>
        <LastName>Петрович</LastName>
    </MainData>

    Создал простую форму WPF с TextBox и ListBox. Далее по рекомендациям создал отдельный класс:

    [Serializable, XmlRoot("MainData")]
       public class DataExctract
        {
            [XmlAttribute("LastName")]
            public string LastName { get; set; }
        }

    Далее, на форме:

    public MainWindow()
            {
                InitializeComponent();
                LoadXml(); //Выполняем метод при загрузке окна
            }
    //Этот метод загружает XML и помещает дочерний элемент Name в ListBox
                public void LoadXml()
            {
                try
                {
                    XElement xElements = XElement.Load(@"DataXML.xml");
    
                    foreach (XElement xEle in xElements.Descendants("Name"))
                    {
                        listBox.Items.Add((string)xEle);
                    }
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
    //При двойном щелчке по любому элементу помещаем фамилию в TextBox
            private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
            {
                try
                {
                    StreamReader reader = new StreamReader(@"DataXML.xml");
                    XmlSerializer serializer = new XmlSerializer(typeof(List<DataExctract>));
                    DataExctract DE = (DataExctract)serializer.Deserialize(reader);
                    textBox.Text = DE.LastName;
                    reader.Close();
                }
    
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
    
            }
        }

    В итоге получаю исключение:

    There is an error in XML document (1, 2)

    Такая ошибка появляется тогда, когда я дважды щёлкаю мышкой по имени.

    Я нашёл 2 подобных вопроса в англоязычном Stack, но не могу понять корень зла (не хватает знаний английского).



    8 февраля 2017 г. 9:49
  • "Я создал ещё более простую по структуре XML'ку:"

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

    <?xml version="1.0"?>
    <PrintEngine xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <ProgramDirectory>C:\Program Files (x86)\Foxit Software\Foxit Reader</ProgramDirectory>
      <ProgramFilename>FoxitReader.exe</ProgramFilename>
      <TimeWait>500</TimeWait>
    </PrintEngine>

    для класса

    [Serializable]
    public class PrintEngine
        {        
    
            public string ProgramDirectory
            {
                get { return program_directory; }
                set { program_directory = value; }
            }
    
            public string ProgramFilename
            {
                get { return program_filename; }
                set { program_filename = value; }
            }
    
            public int TimeWait
            {
                get { return time_wait; }
                set { time_wait = value; }
            }
            
    
        }


    8 февраля 2017 г. 11:51
  • Получаю точно такую же ошибку на строку:

    <PrintEngine xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    9 февраля 2017 г. 5:30
  • А, не увидел что вы создаете сериализатор для списка а скармливаете ему одиночный элемент. 

    В конструкторе XmlSerializer надо передавать тот тип, который десериализуется. Чтобы работать со списком, его надо заверенуть в класс и работать уже с этим классом.



    • Изменено VadimTagil 9 февраля 2017 г. 7:39
    9 февраля 2017 г. 7:39
  • Смотрите, я сделал по вашему подобию:

    Исходный вариант XML:

    <?xml version="1.0"?>
    <Employee xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">            
        <Name>Василий</Name>  
        <LastName>Петрович</LastName> 
    </Employee>


    Name и LastName являются string, верно?

    Создаю новый класс, в котором пишу:

    using System;
    using System.Xml.Serialization;
    
    namespace DemoWPF
    {
        [Serializable, XmlRoot("Employee")]
        public class Employee
        {
            
    
            [XmlElement("Name")]
            public string Name { get; set; }
    
            [XmlElement("LastName")]
            public string LastName { get; set; }
            
        }
    
    }

    В основном файле пишу следующее:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Windows;
    using System.Windows.Input;
    using System.Xml.Linq;
    using System.Xml.Serialization;
    
    namespace DemoWPF
    {
        /// <summary>
        /// Логика взаимодействия для MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                LoadXml();
            }
    
                public void LoadXml()
            {
                try
                {
                    XElement xElements = XElement.Load(@"DataXML.xml");
    
                    foreach (XElement xEle in xElements.Descendants("Name"))
                    {
                        listBox.Items.Add((string)xEle);
                    }
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
    
            private void listBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
            {
    
                #region
                
                try
                {
                    StreamReader reader = new StreamReader("DataXML.xml");
                    XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));
                    Employee DE = (Employee)serializer.Deserialize(reader);
                    textBox.Text = DE.LastName;
                    reader.Close();
                }
    
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                
                #endregion
    
            }
        }
       
    }

    • Изменено DenisAndreevich 9 февраля 2017 г. 9:09 ошибки
    9 февраля 2017 г. 9:03
  • вот это неправильно:

    XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));
    Employee DE = (Employee)serializer.Deserialize(reader);
    надо
    XmlSerializer serializer = new XmlSerializer(typeof(Employee));
    Employee DE = (Employee)serializer.Deserialize(reader);

    • Помечено в качестве ответа DenisAndreevich 9 февраля 2017 г. 11:09
    9 февраля 2017 г. 9:22
  • вот это неправильно:

    XmlSerializer serializer = new XmlSerializer(typeof(List<Employee>));
    Employee DE = (Employee)serializer.Deserialize(reader);
    надо
    XmlSerializer serializer = new XmlSerializer(typeof(Employee));
    Employee DE = (Employee)serializer.Deserialize(reader);

    Дорогой мой человек) Спасибо тебе за время, которые ты уделил мне в помощь :) Сейчас всё работает :) Прежде, чем я помечу твой ответ как правильный, хочу уточнить разницу. Ведь фактически, компилятор ни на что не ругался, а я второй день ходил вокруг да около этих кавычек.
    9 февраля 2017 г. 9:29
  • Во-первых, компилятор не ругался потому что контроль типа сериализатор делает на этапе выполнения

    Во-вторых, разница в том, что List<Employee> это список (точнее, динамический массив) объектов, а Emploee - одиночный объект, т.е. типы разные. Сериализатор обрабатывает только тот тип, который указать при его создании.

    В-третьих, для вашей исходной задачи десериализация, в общем-то, не нужна. Вы можете ее решить также оперируя классом XElement.

    9 февраля 2017 г. 10:48