Всем привет!
Сегодня мы продолжим разбирать параметры сериализации/десериализации.
У модификатор
[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
Этому типу соответствует модификатор
Это позволяет нам сделать класс, который мы обьявляем с этой структурой, назначит главным в 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. В чём преимущество? Мы можем не сериализировать целый класс, и группу свойства, а запихнуть всё что нужно для восстановления состояния например в строку. Иногда на этом экономится довольно много места. а также улучшает читабельность структуры. Давайте немножко изменим предидущий пример.
Заменим модификатор свойства
SubClass на
, чтобы оно не сериализировалось в файл.
Также добавим новое свойство -
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<ObjectBase> ListObject
{
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 ElementName, Type 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. Удачи вам, и стремления к совершенству.