Сериализация в 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>