Показать сообщение отдельно
Старый 11.06.2010, 02:04   #3
Dream
быдло
 
Регистрация: 05.08.2007
Сообщений: 1,435
Написано 614 полезных сообщений
(для 1,489 пользователей)
Радость Полу урок: Сериализация. Части 3. Параметры

Всем привет!
Сегодня мы продолжим разбирать параметры сериализации/десериализации.

У модификатор [XmlAttribute] есть конструктор
XmlAttribute(string AttributeName
Этим самым мы имеем возможность изменить имя атрибута, которое и будет записываться в наш файл. Это может пригодится, если вам потребуется в будущем работать в ручную с этим файлом ( например: вы делаете экспорт каких то данных из своей программы, а потом, вам может понадобится заглянуть в этот файл и что то проверить, или написать новый импортёр для этих данных, имя будет более информативным)
Такойже конструктор есть и у элементов:
XmlElemen(string elementName
Давайте модифицируем наш класс, который мы описывали в прошлой статье.
public class MathClass
    
{
        
#region _fields

        
private string _name;
        [
XmlAttribute("indexOfMathClass")]
        public 
int Id;
        private 
int _firstDigit;
        private 
int _secondDigit;
        
      
#endregion

        #region statics
        
public static int Count;
        
        public static 
int GetNextCount
        
{
            
get
            
{
                
Count++;
                return 
Count;
            }
        }
        
#endregion

        #region C'tos
        
public MathClass()
        {
            
Id GetNextCount;
        }
        
#endregion

        #region Propereties
        
[XmlAttribute("FirstName")]
        public 
string Name
        
{
            
get
            
{
                return 
_name;
            }
            
set
            
{
                
_name value;
            }
        }

        [
XmlElement("DataElementFirst")]
        public 
int SecondDigit
        
{
            
get { return _secondDigit; }
            
set _secondDigit value; }
        }

        [
XmlElement("DataElementSecond")]
        public 
int FirstDigit
        
{
            
get { return _firstDigit; }
            
set _firstDigit value; }
        }
        [
XmlIgnore]
        public 
int Result
        
{
            
get
            
{
                return 
FirstDigit SecondDigit;
            }
            
        }
      
        
#endregion

    

Давайте посмотрим как будет выглядеть после этого наша структура

<MathClass indexOfMathClass="1" FirstName="Xml test class">
  <
DataElementFirst>0</DataElementFirst>
  <
DataElementSecond>0</DataElementSecond>
</
MathClass
Вот то результат которого мы и добивались.

Хотел бы вернутся к первому пункту в списке структур - Root
Этому типу соответствует модификатор
[XmlRoot
Это позволяет нам сделать класс, который мы обьявляем с этой структурой, назначит главным в Xml, и он не сможет выступать в качестве элемента или атрибута, поэтому будьте осторожны.
У него также есть конструктор, который позволяет задавать имя структуры
[XmlRoot(string RootName)] 
Давайте посмотрим, поставим перед объявлением нашего класса модификатор Root
[XmlRoot("Root")]
public class 
MathClass 
Запустите, и посмотрите как изменилась структура в файле.

<Root indexOfMathClass="1" FirstName="Xml test class">
  <
DataElementFirst>0</DataElementFirst>
  <
DataElementSecond>0</DataElementSecond>
</
Root
.

Давайте ещё посмотрим пример, когда у класса есть поле другого класса, объявленного нами

public class ElementClass
    
{
        
#region _fields

        
private string _name;

        
#endregion

        #region C'tors

        
public ElementClass()
        {
            
        }

        
#endregion


        #region Propereties
        
[XmlAttribute("SubClassName")]
        public 
string Name
        
{
            
get { return _name; }
            
set _name value; }
        }

        
#endregion

    

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

private ElementClass _subClass;

 [
XmlElement]
        public 
ElementClass SubClass
        
{
            
get { return _subClass; }
            
set _subClass value; }
        } 

а также изменим немножко нашу основную функцию

static void Main(string[] args)
        {
            
MathClass myClass=new MathClass();
            
myClass.Name "Xml test class";
            
XmlHelper.WriteEntity(myClass,@"C:\classes.xml");

            
myClass = new MathClass();
            
myClass.Name "Xml test class";
            
myClass.SubClass=new ElementClass();
            
myClass.SubClass.Name "This is Sub Class!";
            
XmlHelper.WriteEntity(myClass, @"C:\classes1.xml");

            
MathClass deserializeClass XmlHelper.GetEntity<MathClass>(@"C:\classes.xml");
            
Console.Write(deserializeClass.Id+": name - "+deserializeClass.Name);
        } 
В этом коде мы присвоим свойству SubClass новый экземпляр этого класса.И присвоим ему имя.
После сериализации мы увидим что в файле classes1, появилось свойство SubClass.
Как видим поля, которые равны null, не сериализируется, если поле числового типа, то оно сериализируется в любом случае.

Теперь хочу кое что сказать по поводу свойств, о их преймуществах при сериализации по сравнению с полями и членами класса(field,members).
У свойств есть два поля -
get{} 
и 
set
{} 
при сериализации, для получения значения свойства вызывается поле get, при десериализации данные из файла передаются в поле set. В чём преимущество? Мы можем не сериализировать целый класс, и группу свойства, а запихнуть всё что нужно для восстановления состояния например в строку. Иногда на этом экономится довольно много места. а также улучшает читабельность структуры. Давайте немножко изменим предидущий пример.

Заменим модификатор свойства SubClass на
[XmlIgnore]
, чтобы оно не сериализировалось в файл.
Также добавим новое свойство - SubClassName:
[XmlAttribute("SubClassName")]
        public 
string SubClassName
        
{
            
//Вызывается при сериализации объкта
            
get
            
{
                
string retValue string.Empty;
                if (
_subClass != null)
                {
                    
retValue _subClass.Name;
                }
                return 
retValue;
            }
            
//Вызывается при десериализации объекта
            
set
            
{
                if (
_subClass == null)
                {
                    
_subClass = new ElementClass();
                }
                
_subClass.Name value;
            }


        } 
Теперь если вы посмотрите в xml структуру, то увидете что у структуры Root появился атрибут SubClassName, который содержит имя.

Далее...
Отступление.
Так как Си-шарп поддерживет ООП в полноймере, есть возможность зоздавать классы, наследуя их от других классов и интерфейсов. Часто бывает что нужно сериализовать множество разных классов, которые имеют общие поля. При использовании ООП, нет необходимости создвать несколько списков, для содержания разных типов обьектов. Тоесть движок игры, который обновляет состояния всех обьектов, а обьекты в свою очередь могут будь разных типов - 3д обьекты, 2д обькты, скрипты. Без ооп, нам бы пришлось держать 3 списка обьктов, чтобы обновлять их.

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

public abstract class ObjectBase
    
{
        private 
string _name;

        [
XmlAttribute]
        public 
string Name
        
{

            
get
            
{
                return 
_name;
            }
            
set
            
{
                
_name value;
            }
        }
       public 
virtual void Update(int time)
        {

        }

    }

    public class 
Object3D:ObjectBase
    
{
        
#region _fields

        
private string _only3DParameter;
        
#endregion


        #region Propereties
       
public Object3D()
        {
            
Name "Object3D";
            
_only3DParameter "Parameters 3D";
        }
       

        public 
string Only3DParameter
        
{
            
get { return _only3DParameter; }
            
set _only3DParameter value; }
        }

        
#endregion

        #region Methods

        
public override void Update(int time)
        {
            
        }

        
#endregion

    
}

    public class 
Object2D ObjectBase
    
{
        
#region _fields

        
private string _only2DParameter;
        
#endregion

        #region C'tors
        
public Object2D()
        {
            
Name "Object2D";
            
_only2DParameter "Parameters 2D";
        }


        
#endregion

        #region Propereties

        

        
public string Only2DParameter
        
{
            
get { return _only2DParameter; }
            
set _only2DParameter value; }
        }

        
#endregion

        #region Methods

        
public override void Update(int time)
        {
            
        }

        
#endregion

    
}
    [
XmlRoot("SaveGame")]
    public class 
ForSerialize
    
{
        private List<
ObjectBase_listObject=new List<ObjectBase>();
        public 
ForSerialize()
        {
            
        }

        [
XmlElement("Object2D"typeof(Object2D))]
        [
XmlElement("Object3D"typeof(Object3D))]
        public List<
ObjectBaseListObject
        
{
            
get { return _listObject; }
            
set _listObject value; }
        }
        public 
void Update(int time)
        {
            
_listObject.ForEach(delegate(ObjectBase obj)
                                    {
                                        
obj.Update(time);
                                    }
                                    );
        }


    } 
Обьясню немного. ObjectBase - базовый класс для всех наших обьктов, как 2д так и 3д, содержит поля имя обьекта, и метод для обновления обьекта. Object2D и Object3D я создал просто для примера. ForSerialize - менеджер объектов, который будет обновлять их.

Ткпкрь посмотрите на параметры модификатора XmlElementв классе ForSerialize . как видите, мы обьявили два модификатора для одного свойства объекта. Конструктор его такой -
[XmlElement(string ElementNameType type)] 
Если при сериализации типа объекта будет равен type, то его имя при сериализации будет ElementName.
Это нужно для того, чтобы восстанавливать тип обьктов, от общего родителя как у нас. тоесть чтобы при сериализации Object3D десериализовался как Object3D а не просто ObjectBase.

Изменим основной метод нашей программы,
ForSerialize serialize=new ForSerialize();
            
Object3D object3D=new Object3D();
            
object3D.Name "NewObject3D - 1";
            
serialize.ListObject.Add(object3D);

            
Object2D object2D=new Object2D();
            
object2D.Name "NewObject 2D - 1";
            
serialize.ListObject.Add(object2D);

            
object3D = new Object3D();
            
object3D.Name "NewObject3D - 2";
            
serialize.ListObject.Add(object3D);
            
            
XmlHelper.WriteEntity(serialize,@"C:\Serialize.xml"); 
Здесь мы заполняем экземпляр класса ForSerialize 2D и 3D объектами, а потом сериализуем его в файл @"C:\Serialize.xml".
Структура этого файла будет выглядеть так -
<ForSerialize>
  <
Object3D Name="NewObject3D - 1">
    <
Only3DParameter>Parameters 3D</Only3DParameter>
  </
Object3D>
  <
Object2D Name="NewObject 2D - 1">
    <
Only2DParameter>Parameters 2D</Only2DParameter>
  </
Object2D>
  <
Object3D Name="NewObject3D - 2">
    <
Only3DParameter>Parameters 3D</Only3DParameter>
  </
Object3D>
</
ForSerialize
Вот вобщемто и всё, что я хотел рассказать про сериализации, ещё наврено много нюансов осталось не описаными, но лучше если вы сами их найдёте.
Со временем я возможно буду добавлять заметки в эту статью.
Я прикрепил к посту проект, который был использован в последнем примере.
В ближайшем будующем я возможно напишу статью по XNA. Удачи вам, и стремления к совершенству.
Вложения
Тип файла: rar SerializeTest.rar (3.3 Кб, 760 просмотров)
(Offline)
 
Ответить с цитированием
Эти 7 пользователя(ей) сказали Спасибо Dream за это полезное сообщение:
den (05.08.2010), h1dd3n (02.09.2010), Harter (12.06.2010), Lestar (18.06.2011), pax (11.06.2010), Radnk (25.08.2012), Randomize (26.05.2011)