Сериализация в XML. XmlSerializer
Для удобного сохранения и извлечения объектов из файлов xml может использоваться класс XmlSerializer из пространства имен System.Xml.Serialization. Данный класс упрощает сохранение сложных объектов в формат xml и последующее их извлечение.
Для создания объекта XmlSerializer можно применять различные конструкторы, но почти все они требуют указания типа данных, которые будут сериализоваться и десериализоваться:
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));
//[Serializable]
class Person { }
В данном случае XmlSerializer будет работать только с объектами класса Person.
Следует учитывать, что XmlSerializer предполагает некоторые ограничения. Например, класс, подлежащий сериализации, должен иметь стандартный конструктор без параметров и должен иметь модификатор доступа public. Также сериализации подлежат только открытые члены. Если в классе есть поля или свойства с модификатором private, то при сериализации они будут игнорироваться. Кроме того, свойства должны иметь общедоступные геттеры и сеттеры.
Сериализация
Для сериализации (то есть сохранения в форма xml) применяется метод Serialize(). Данный метод имеет ряд версий. Возьмем самую простую из них:
void Serialize (Stream stream, object? o);
В качестве первого параметра передается поток Stream (например, объект FileStream), в который будет идти сериализация. А второй параметр представляет собственно тот объект, который будет сохраняться в формат xml. Например:
using System.Xml.Serialization;
// объект для сериализации
Person person = new Person("Tom", 37);
// передаем в конструктор тип класса Person
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));
// получаем поток, куда будем записывать сериализованный объект
using (FileStream fs = new FileStream("person.xml", FileMode.OpenOrCreate))
{
xmlSerializer.Serialize(fs, person);
Console.WriteLine("Object has been serialized");
}
//[Serializable]
public class Person
{
public string Name { get; set; } = "Undefined";
public int Age { get; set; } = 1;
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Итак, класс Person общедоступный, имеет общедоступные свойства и конструктор без параметров, поэтому объекты этого класса подлежат сериализации. При создании объекта XmlSerializer передаем в конструктор тип класса Person.
В метод Serialize передается файловый поток для сохранения данных в файл person.xml и сохраняемый в этот файл объект Person. И если по завершению программы мы откроем файл person.xml, то увидим содержание нашего объекта:
<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Name>Tom</Name>
<Age>37</Age>
</Person>
Десериализация
Для десериализации данных xml в объект кода C# применяется метод Deserialize(). Отметим одну из версий этого метода:
object? Deserialize (Stream stream);
В качестве параметра в метод передается объект Stream, который содержит данные в формате xml. Результатом метода является десериализованный объект.
Например, десериализуем данные из выше созданного файла person.xml:
using System.Xml.Serialization;
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Person));
// десериализуем объект
using (FileStream fs = new FileStream("person.xml", FileMode.OpenOrCreate))
{
Person? person = xmlSerializer.Deserialize(fs) as Person;
Console.WriteLine($"Name: {person?.Name} Age: {person?.Age}");
}
public class Person
{
public string Name { get; set; } = "Undefined";
public int Age { get; set; } = 1;
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Сериализация и десериализация коллекций
Подобным образом мы можем сериализовать массив или коллекцию объектов:
using System.Xml.Serialization;
Person[] people = new Person[]
{
new Person("Tom", 37),
new Person("Bob", 41)
};
XmlSerializer formatter = new XmlSerializer(typeof(Person[]));
// сохранение массива в файл
using (FileStream fs = new FileStream("people.xml", FileMode.OpenOrCreate))
{
formatter.Serialize(fs, people);
}
// восстановление массива из файла
using (FileStream fs = new FileStream("people.xml", FileMode.OpenOrCreate))
{
Person[]? newpeople = formatter.Deserialize(fs) as Person[];
if(newpeople != null)
{
foreach (Person person in newpeople)
{
Console.WriteLine($"Name: {person.Name} --- Age: {person.Age}");
}
}
}
public class Person
{
public string Name { get; set; } = "Undefined";
public int Age { get; set; } = 1;
public Person() { }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Консольный вывод:
Name: Tom --- Age: 37
Name: Bob --- Age: 41
Но это был простой объект. Однако с более сложными по составу объектами работать так же просто. Например:
using System.Xml.Serialization;
var microsoft = new Company("Microsoft");
var google = new Company("Google");
Person[] people = new Person[]
{
new Person("Tom", 37, microsoft),
new Person("Bob", 41, google)
};
XmlSerializer formatter = new XmlSerializer(typeof(Person[]));
using (FileStream fs = new FileStream("people.xml", FileMode.OpenOrCreate))
{
formatter.Serialize(fs, people);
}
using (FileStream fs = new FileStream("people.xml", FileMode.OpenOrCreate))
{
Person[]? newpeople = formatter.Deserialize(fs) as Person[];
if(newpeople != null)
{
foreach (Person person in newpeople)
{
Console.WriteLine($"Name: {person.Name}");
Console.WriteLine($"Age: {person.Age}");
Console.WriteLine($"Company: {person.Company.Name}");
}
}
}
public class Company
{
public string Name { get; set; } = "Undefined";
// стандартный конструктор без параметров
public Company() { }
public Company(string name) => Name = name;
}
public class Person
{
public string Name { get; set; } = "Undefined";
public int Age { get; set; } = 1;
public Company Company { get; set; } = new Company();
public Person() { }
public Person(string name, int age, Company company)
{
Name = name;
Age = age;
Company = company;
}
}
Класс Person содержит свойство Company, которое будет хранить объект класса Company. Члены класса Company объявляются с модификатором public, кроме того также присутствует стандартный конструктор без параметров. В итоге после сериализации мы получим следующий xml-документ:
<?xml version="1.0"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Person>
<Name>Tom</Name>
<Age>37</Age>
<Company>
<Name>Microsoft</Name>
</Company>
</Person>
<Person>
<Name>Bob</Name>
<Age>41</Age>
<Company>
<Name>Google</Name>
</Company>
</Person>
</ArrayOfPerson>