none
Сериализация списка List<T> RRS feed

  • Вопрос

  • Продолжение вопроса Сериализация значений WPF PropertyGrid. Я решила использовать foreach для определения того какие наследники класса с PropertyGrid будут сериализовываться, так как это не известно заранее. 

    Есть такой метод:

    public bool Save(Tube m_tube, Intersection m_intersection, string m_fileName)
            {
                var settings = new XmlWriterSettings { Indent = true };
                using (var writer = XmlWriter.Create(m_fileName, settings))
                {
                    writer.WriteStartElement("Part");
    
                    var ser = new XmlSerializer(typeof(Tube));
                    ser.Serialize(writer, m_tube);
    
                    ArrayList res = new ArrayList();
    
                    foreach (IntersectionListItem de in MainViewModel.GetInstance().IntersectionView._IntersectionRepository._Intersections)
                    {
                        res.Add(((Intersection)de._Intersection)._IntersectionType.GetType());
    
                        ser = new XmlSerializer(typeof(Intersection), (Type[])res.ToArray(typeof(Type)));
                        ser.Serialize(writer, m_intersection);
                    }
    
                    writer.WriteEndElement();
                }

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

    <?xml version="1.0" encoding="utf-8"?>
    <Part>
      <Tube xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="OvalTube">
        <Type>OvalTube</Type>
        <WallThickness>0</WallThickness>
        <Height>0</Height>
        <Width>0</Width>
      </Tube>
      <Intersection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ConeIntersection">
        <Type>ConeIntersection</Type>
        <Position>Left</Position>
        <Cut>InnerCut</Cut>
        <Movement>Robot</Movement>
        <Burning>Cutting</Burning>
      </Intersection>
      <Intersection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ConeIntersection">
        <Type>ConeIntersection</Type>
        <Position>Left</Position>
        <Cut>InnerCut</Cut>
        <Movement>Robot</Movement>
        <Burning>Cutting</Burning>
      </Intersection>
      <Intersection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ConeIntersection">
        <Type>ConeIntersection</Type>
        <Position>Left</Position>
        <Cut>InnerCut</Cut>
        <Movement>Robot</Movement>
        <Burning>Cutting</Burning>
      </Intersection>
    </Part>

    Хотя должны создатся три разных объекта в тегах Intersection. Что я делаю не так?

    По идеи должен создаться вот такой файл:

    <?xml version="1.0" encoding="utf-8"?>
    <Part>
      <Tube xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="OvalTube">
        <Type>OvalTube</Type>
        <WallThickness>0</WallThickness>
        <Height>0</Height>
        <Width>0</Width>
      </Tube>
      <Intersection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ConeIntersection">
        <Type>ConeIntersection</Type>
        <Position>Left</Position>
        <Cut>InnerCut</Cut>
        <Movement>Robot</Movement>
        <Burning>Cutting</Burning>
      </Intersection>
      <Intersection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ConeIntersection">
        <Type>PlaneIntersection</Type>
        <Position>Left</Position>
        <Cut>InnerCut</Cut>
        <Movement>Robot</Movement>
        <Burning>Cutting</Burning>
      </Intersection>
      <Intersection xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ConeIntersection">
        <Type>TubeIntersection</Type>
        <Position>Left</Position>
        <Cut>InnerCut</Cut>
        <Movement>Robot</Movement>
        <Burning>Cutting</Burning>
      </Intersection>
    </Part>
    • Изменено nigina_1989 3 декабря 2012 г. 10:57
    3 декабря 2012 г. 10:53

Ответы

  • nigina_1989, вы приводите массу лишних сведений, в то же время не давая нужных. Например, нам совершенно не важно знать, что у вас используется PropertyGrid - это визуальная часть приложения (представление), а сериализация работает с данными (модель).

    В последнем примере кода достаточно было привести кусок кода внутри using. Всё остальное - создание и работа с OpenFileDialog - не нужно, оно только отвлекает наше внимание, пропадает желание смотреть портянки кода.

    Так, возвращаюсь к главному вопросу. Мне кажется, достаточно будет в самом первом примере (из первого сообщения темы) переписать цикл так:

    foreach (IntersectionListItem de in MainViewModel.GetInstance().IntersectionView._IntersectionRepository._Intersections)
    {
        ser = new XmlSerializer(typeof(Intersection));
        ser.Serialize(writer, (Intersection)de._Intersection);
    }

    Не? Могу ошибаться, потому что не знаю точного типа перечисляемой коллекции.

     

    P.S. Не могу не поворчать. Вы всё дальше и дальше уходите от основного предназначения сериализации - автоматической работы с данными. Вместо декларативных настроек сериализуемых данных появляется всё больше вручную написанного кода. Тут два выхода: нужно либо пересмотреть модель данных, и создать некий объединяющий класс (как вам предлагалось в предыдущих темах), либо отказаться от сериализации, и сохранять/читать данные вручную (XmlWriter/XmlReader). Ибо моё чувство прекрасного ущемлено.

    То же чувство прекрасного не даёт спокойно смотреть на разнокалиберный нэйминг переменных. Вот зачем у параметров метода префиксы m_ (m_tube, m_intersection) ? Обычно m добавляют к именам членов (member) класса (если вообще добавляют). У вас в конторе всё ещё не используют StyleCop?

    Также мне не даёт покоя многократное создание сериализаторов - ведь это долгий процесс! Мне кажется, у вас разные сериализаторы создаются часто. Генерация кода для них идёт через рефлексию, что очень медленно, потом этот код компилируется, созданные сборки не выгружаются, занимают память... Поэтому, желательно сериализаторы кэшировать. Суть смотрите здесь, раздел "Динамически созданные сборки".

    • Помечено в качестве ответа nigina_1989 4 декабря 2012 г. 9:35
    3 декабря 2012 г. 23:26

Все ответы

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

    А зачем вы спсиок сериализуете после добавления в него каждого элемента? Не надо, случаем, сераилизацию вынести за цикл?

    3 декабря 2012 г. 11:17
    Отвечающий
  • Пробовала вынести сериализацию, этот кусочек кода:

     ser = new XmlSerializer(typeof(Intersection), (Type[])res.ToArray(typeof(Type)));
                    ser.Serialize(writer, m_intersection);

    за цикл, но так сохраняется только один объект Intersection

    3 декабря 2012 г. 11:25
  • Попробуйте вот так

    var value = MainViewModel.GetInstance().IntersectionView._IntersectionRepository._Intersections;
    var ser = new XmlSerializer(typeof(value));
    ser.Serialize(writer, value);

    3 декабря 2012 г. 12:06
  • Попробуйте вот так

    var value = MainViewModel.GetInstance().IntersectionView._IntersectionRepository._Intersections;
    var ser = new XmlSerializer(typeof(value));
    ser.Serialize(writer, value);

    Ваш вариант работает создаётся файл с тремя разными объектами, но проблема теперь в том, что я не  могу прочитать файл, так как создаётся дополнительный тег. Файл который создался вашим способом:

    <?xml version="1.0" encoding="utf-8"?>
    <Part>
      <Tube xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="OvalTube">
        <Type>OvalTube</Type>
        <WallThickness>80</WallThickness>
        <Height>580</Height>
        <Width>80</Width>
      </Tube>
      <ArrayOfIntersectionListItem xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <IntersectionListItem>
          <_Intersection xsi:type="PlaneIntersection">
            <Type>PlaneIntersection</Type>
            <Position>Left</Position>
            <Cut>InnerCut</Cut>
            <Movement>Robot</Movement>
            <Burning>Cutting</Burning>
          </_Intersection>
        </IntersectionListItem>
        <IntersectionListItem>
          <_Intersection xsi:type="TubeIntersection">
            <Type>TubeIntersection</Type>
            <Position>Left</Position>
            <Cut>InnerCut</Cut>
            <Movement>Robot</Movement>
            <Burning>Cutting</Burning>
            <_TubeDiameter>0</_TubeDiameter>
          </_Intersection>
        </IntersectionListItem>
        <IntersectionListItem>
          <_Intersection xsi:type="ConeIntersection">
            <Type>ConeIntersection</Type>
            <Position>Left</Position>
            <Cut>InnerCut</Cut>
            <Movement>Robot</Movement>
            <Burning>Cutting</Burning>
          </_Intersection>
        </IntersectionListItem>
      </ArrayOfIntersectionListItem>
    </Part>

    Ссылается на то что не может найти  ArrayOfIntersectionListItem. Для чтения сделала следующее:

    void Open() { bool fileOpened = false; OpenFileDialog openDialog = new OpenFileDialog(); FolderBrowserDialog folderBrowse = new FolderBrowserDialog(); openDialog.InitialDirectory = @"C:\Users\ninochka\Desktop"; openDialog.DefaultExt = "xml"; openDialog.Filter = "Text documents (.xml)|*.xml"; folderBrowse.Description = "Select the directory that you want to use as the default."; if (!fileOpened) { openDialog.InitialDirectory = folderBrowse.SelectedPath; openDialog.FileName = null; } DialogResult result = openDialog.ShowDialog(); if (result == DialogResult.OK) { _projectFolder = openDialog.FileName; try { using (var reader = XmlReader.Create(_projectFolder)) { reader.ReadToFollowing("Tube"); var ser = new XmlSerializer(typeof(Tube)); PartView._Tube = (Tube)ser.Deserialize(reader); reader.ReadStartElement("ArrayOfIntersectionListItem");

    reader.ReadStartElement("IntersectionListItem"); ser = new XmlSerializer(typeof(Intersection)); IntersectionView._Intersection = (Intersection)ser.Deserialize(reader); } fileOpened = true; } catch (Exception exp) { MessageBox.Show("An error occurred while attempting to load the file. The error is: " + System.Environment.NewLine + exp.ToString() + System.Environment.NewLine); fileOpened = false; } } else if (result == DialogResult.Cancel) { return; } }

    Но теперь он читает только первый попавшийся элемент, остальные два он не может прочитать.

    3 декабря 2012 г. 13:41
  • Вам надо в цикле читать ваши значения, так как сделанно у вас сейчас он читаеть только 1 элемент

    3 декабря 2012 г. 13:55
  • nigina_1989, вы приводите массу лишних сведений, в то же время не давая нужных. Например, нам совершенно не важно знать, что у вас используется PropertyGrid - это визуальная часть приложения (представление), а сериализация работает с данными (модель).

    В последнем примере кода достаточно было привести кусок кода внутри using. Всё остальное - создание и работа с OpenFileDialog - не нужно, оно только отвлекает наше внимание, пропадает желание смотреть портянки кода.

    Так, возвращаюсь к главному вопросу. Мне кажется, достаточно будет в самом первом примере (из первого сообщения темы) переписать цикл так:

    foreach (IntersectionListItem de in MainViewModel.GetInstance().IntersectionView._IntersectionRepository._Intersections)
    {
        ser = new XmlSerializer(typeof(Intersection));
        ser.Serialize(writer, (Intersection)de._Intersection);
    }

    Не? Могу ошибаться, потому что не знаю точного типа перечисляемой коллекции.

     

    P.S. Не могу не поворчать. Вы всё дальше и дальше уходите от основного предназначения сериализации - автоматической работы с данными. Вместо декларативных настроек сериализуемых данных появляется всё больше вручную написанного кода. Тут два выхода: нужно либо пересмотреть модель данных, и создать некий объединяющий класс (как вам предлагалось в предыдущих темах), либо отказаться от сериализации, и сохранять/читать данные вручную (XmlWriter/XmlReader). Ибо моё чувство прекрасного ущемлено.

    То же чувство прекрасного не даёт спокойно смотреть на разнокалиберный нэйминг переменных. Вот зачем у параметров метода префиксы m_ (m_tube, m_intersection) ? Обычно m добавляют к именам членов (member) класса (если вообще добавляют). У вас в конторе всё ещё не используют StyleCop?

    Также мне не даёт покоя многократное создание сериализаторов - ведь это долгий процесс! Мне кажется, у вас разные сериализаторы создаются часто. Генерация кода для них идёт через рефлексию, что очень медленно, потом этот код компилируется, созданные сборки не выгружаются, занимают память... Поэтому, желательно сериализаторы кэшировать. Суть смотрите здесь, раздел "Динамически созданные сборки".

    • Помечено в качестве ответа nigina_1989 4 декабря 2012 г. 9:35
    3 декабря 2012 г. 23:26
  • Petalvik спасибо за ваш ответ. Дело в том что когда я ещё начала писать сериализтор пока не было графического интерфейса. Графичечкий интерфейс создали совсем другие люди, и теперь мне приходится переписывать то что я делала до этого, так как графический интерфейс очень запутанный.  Что касается префикса m_(m_tube) так решили сделать другие программисты которые начали писать графический интерфейс. И я ничего не могу поделать с этим, так как большинство переменных имеют префикс m_. Я сама использую StyleCop а вот другие нет. 

    В любом случае спасибо за вашу помочь. Надеюсь больше не возникнет проблем.

    4 декабря 2012 г. 9:22