В
предыдущей статье мы получили такой код :
public class XmlHelper
{
#region members
private static readonly Hashtable hash = new Hashtable();
#endregion
#region Static
public static XmlSerializer GetSerializer(Type type)
{
XmlSerializer res;
lock (hash)
{
res = hash[type.FullName] as XmlSerializer;
if (res == null)
{
res = new XmlSerializer(type);
hash[type.FullName] = res;
}
}
return res;
}
public static void WriteEntity(object obj, string fileFullPath)
{
XmlSerializer serializer = GetSerializer(obj.GetType());
StringBuilder stringBuilder = new StringBuilder();
StringWriter w = new StringWriter(stringBuilder, CultureInfo.InvariantCulture);
serializer.Serialize(w, obj);
w.Close();
string xml = stringBuilder.ToString();
StreamWriter sw = new StreamWriter(fileFullPath);
sw.Write(xml);
sw.Close();
}
#endregion
}
Сегодня давайте приступим к десериализации, то есть в восстановлению класса из xml структуры, записанной ранее нами.
Это мы будем делать с помощью метода
T GetEntity<T>(string fileFullPath)
, где
Т - типа возвращаемого объекта, а string fileFullPath - путь к файлу из которого требуется извлечь структуру.
Выглядеть метод будет так
using (StreamReader reader = new StreamReader(fileFullPath))
{
XmlSerializer dsr = GetSerializer(typeof(T));
return (T)deSerializer.Deserialize(reader);
}
using (StreamReader reader = new StreamReader(fileFullPath))
{
мы создаём Потоковый ридер из файла, который мы получили в параметрах.
XmlSerializer deSerializer= GetSerializer(typeof(T));
Так же как и в предыдущем методе, мы получаем сериализатор для даного типа.
в следующей строке, мы производим десериализацию из потока reader, с помощью полученного нами сериализатора
deSerializer, и приводим его у требовавшемуся типу -
Т который был указан при вызове метода:
return (T)deSerializer.Deserialize(reader);
Вот собственно и всё! теперь можно записывать и получать экземпляры классов с помощью нашего помощника.
Вот пример кода:
Объявляем класс
TempClass:
public class TempClass
{
#region _fields
private string _name;
public int Id;
#endregion
#region statics
public static int Count;
public static int GetNextCount
{
get
{
return ++Count;
}
}
#endregion
#region C'tos
public TempClass()
{
Id = GetNextCount;
}
#endregion
#region Propereties
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
#endregion
}
Здесь я ввёл глобальные статические поля, что показать как работает сериализатор. при создании экземпляра класса
TempClass, количество экземпляров увеличивается, что даёт нам возможность узнать какой по счёту этот экземпляр.
Далее:
class Program
{
static void Main(string[] args)
{
TempClass myClass=new TempClass();
myClass.Name = "Xml test class";
XmlHelper.WriteEntity(myClass,@"C:\classes.xml");
myClass = new TempClass();
myClass.Name = "Xml test class 2";
XmlHelper.WriteEntity(myClass, @"C:\classes1.xml");
TempClass deserializeClass = XmlHelper.GetEntity<TempClass>(@"C:\classes.xml");
Console.Write(deserializeClass.Id+": name - "+deserializeClass.Name);
}
}
Примечание: пример был создан на основе шаблона -
Console application.
TempClass myClass=new TempClass();
myClass.Name = "Xml test class";
Создаём экземпляр класса
TempClass, и назначаем ему имя -
"Xml test class"
Далее вызываем наш метотод для сериализации в файл
@"C:\classes.xml":
XmlHelper.WriteEntity(myClass,@"C:\classes.xml");
Также повторяем для ещё одного экземпляра, для чистоты эксперимента:
myClass = new TempClass();
myClass.Name = "Xml test class 2";
XmlHelper.WriteEntity(myClass, @"C:\classes1.xml");
Теперь, если посмотреть на диске C: вы увидите два Xml файла со структурой
<TempClass >
<Id>1</Id>
<Name>Xml test class</Name>
</TempClass>
Как в видите статические поля не сериализуюся.
Также поля, помеченные как приватные (а также как protected), тоже не сериализуются.
Остальные поля сериализовались с именем прописанном в коде.
В первом файле, поле
Id будет равно 1, а во втором - 2, то есть можно быть уверенным что сериализовалось два разных экземпляра.
Идём дальше по коду.
TempClass deserializeClass = XmlHelper.GetEntity<TempClass>(@"C:\classes.xml");
Здесь мы вызываем метод десериализации написанный нами в начале этой статьи. Пытаемся десериализировать файл
"classes.xml".
Примечание: при десериализцаии вызывается конструктор по умолчанию (
new()) который обязательно должен присутствовать в сериализуемом классе, также класс, который вы ходите сериализовать/десериализовать должен быть объявлен с модификатором видимости
public.
и свойства полученного нами экземпляра в консоль, чтобы наглядно увидеть.
Console.Write(deserializeClass.Id+": name - "+deserializeClass.Name);
в строке вы увидите что мы десериализовали из файла класс, который был объявлен и записан первым в файл
classes.xml .
3.
А сейчас давайте поговорим о нюансах и некоторых возможностях серилизации/десериализации.
и так:
1)
Возможность исключения из сериализации/десериализации не нужных полей:
[XmlIgnore]
ставится перед полем/свойством, которое мы
не хотим чтобы сериализировалось в файл.
Это может понадобится если ваш класс выполняет какие действия в свойствах, например, модифицируем на класс
TempClass в класс
MathClass и добавим ему несколько полей.
public class MathClass
{
#region _fields
private string _name;
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
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public int SecondDigit
{
get { return _secondDigit; }
set { _secondDigit = value; }
}
public int FirstDigit
{
get { return _firstDigit; }
set { _firstDigit = value; }
}
[XmlIgnore]
public int Result
{
get
{
return FirstDigit + SecondDigit;
}
}
#endregion
}
Этот класс, при вызове получении свойства
Result возвращает сумму двух полей -
FirstDigit и
SecondDigit. но так как результат у нас обновляется автоматически нам нет надобности записывать его в файл, и мы ставим перед ним
[XmlIgnore]. остальные же поля будут сериализованы в файл.
2)
В xml 3 типа структур:
-
ROOT - коренная структура, в которую входят все под структуры. В этой роли выступает класс, который был передан на сериализацию. в нашем примере это класс
MathClass. Посмотрите в xml, и вы увидите что основная структура тоже называется
MathClass, это корень.
-
Attribute - поля, которые будут записываться перед ">" начала текущей структуры.
- Element - под структуры в текущей структуре - по
умолчанию, для всех свойств/полей класса при сериализации. В общем то, представляет собой сериализацию того класса, который содержится в этом самом поле/свойстве
Пример:
<MathClass Id="2" Name="Xml test class"> <!--Id и Name - атрибуиты структуры-->
<SecondDigit>0</SecondDigit><!--Эллементы-->
<FirstDigit>0</FirstDigit><!--Эллементы-->
</MathClass>
Давайте попробуем изменить наш класс
MathClass, чтобы получить такую структуру, как в примере.
Поставим перед полем
Id модификатор
[XmlAttribute]
public int Id;
, чтобы сделать его атрибутом класса. Также поступим и со свойством
Name
[XmlAttribute]
public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
.
Теперь установим свойства
SecondDigit и
FirstDigit элементами нашего класса
[XmlElement]
public int SecondDigit
{
get { return _secondDigit; }
set { _secondDigit = value; }
}
[XmlElement]
public int FirstDigit
{
get { return _firstDigit; }
set { _firstDigit = value; }
}
Вот и всё, теперь мы получим точна такую же структуру как в примере.
На сегодня всё, мы разобрались как считать структуру из файла, смогли установить какие поля нам не нужны при сохранении, а также, как делать нужные нам свойства полями.
В следующий раз я разберу как назначать мена сериализируемым объектам и ещё несколько нюансов, которые возникают при этом. До скорых!