Код сериализации объектов
생성일: 2025년 1월 18일
생성일: 2025년 1월 18일
Я изучаю код. Объясни его как можно более подробно.
Задание, выполняющий код:
// Общий интерфейс для сериализаторов
public interface ISerializer
{
string Serialize(object obj);
T Deserialize<T>(string data);
}
// Класс с наследованием, содержащий запрещенное поле для сериализации
[Serializable]
public class Country
{
public string Name { get; set; }
public int Rank { get; set; }
[NonSerialized] // Запрет на сериализацию этого поля
public string Password;
textpublic Country() { } // Пустой конструктор для десериализации public Country(string name, int number, string password) { Name = name; Rank = number; Password = password; }
}
[Serializable]
public class CountryList
{
public Country[] Countries { get; set; } // Использование массива вместо List
textpublic CountryList() { Countries = new Country[0]; // Инициализация пустым массивом } public static CountryList FromList(List<Country> countryList) { return new CountryList { Countries = countryList.ToArray() }; // Преобразование в массив } public List<Country> ToList() { return new List<Country>(Countries); // Преобразование обратно в список }
}
// Сериализатор для JSON
public class JsonSerializer : ISerializer
{
public string Serialize(object obj) => JsonConvert.SerializeObject(obj);
public T Deserialize<T>(string data) => JsonConvert.DeserializeObject<T>(data);
}
// Сериализатор для XML
public class XmlSerializer : ISerializer
{
public string Serialize(object obj)
{
var xmlSerializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
var stringWriter = new StringWriter();
xmlSerializer.Serialize(stringWriter, obj);
return stringWriter.ToString();
}
textpublic T Deserialize<T>(string data) { var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); var stringReader = new StringReader(data); return (T)xmlSerializer.Deserialize(stringReader); }
}
// Сериализатор для SOAP
public class SoapSerializer : ISerializer
{
public string Serialize(object obj)
{
using (var memoryStream = new MemoryStream())
{
IFormatter formatter = new SoapFormatter();
formatter.Serialize(memoryStream, obj);
return Convert.ToBase64String(memoryStream.ToArray());
}
}
textpublic T Deserialize<T>(string data) { var bytes = Convert.FromBase64String(data); using (var memoryStream = new MemoryStream(bytes)) { IFormatter formatter = new SoapFormatter(); return (T)formatter.Deserialize(memoryStream); } }
}
public class Program
{
public static void Main()
{
var countryList = new List<Country>
{
new Country("Russia", 10, "secret_password"),
new Country("Belarus", 20, "another_password"),
new Country("China", 5, "mypassword")
};
text// Преобразуем список в класс-обертку var wrappedCountryList = CountryList.FromList(countryList); // Используем SOAP сериализатор ISerializer soapSerializer = new SoapSerializer(); string soapData = soapSerializer.Serialize(wrappedCountryList); Console.WriteLine("SOAP Serialized: " + soapData); // Запись SOAP в файл File.WriteAllText("countrys.soap", soapData); // Десериализация коллекции var deserializedCountryList = soapSerializer.Deserialize<CountryList>(soapData); Console.WriteLine("Deserialized Countries from SOAP:"); // Печатаем результат десериализации foreach (var country in deserializedCountryList.ToList()) { Console.WriteLine($"Name: {country.Name}, Rank: {country.Rank}, Password: {country.Password}"); } // Используем JSON сериализатор ISerializer jsonSerializer = new JsonSerializer(); string jsonData = jsonSerializer.Serialize(countryList); Console.WriteLine("JSON Serialized: " + jsonData); // Запись JSON в файл File.WriteAllText("countrys.json", jsonData); // Чтение JSON из файла и десериализация string jsonFromFile = File.ReadAllText("countrys.json"); var deserializedPeopleJson = jsonSerializer.Deserialize<List<Country>>(jsonFromFile); Console.WriteLine("Deserialized from JSON:"); foreach (var countrie in deserializedPeopleJson) { Console.WriteLine($"{countrie.Name}, {countrie.Rank}"); } // Используем XML сериализатор ISerializer xmlSerializer = new XmlSerializer(); string xmlData = xmlSerializer.Serialize(countryList); Console.WriteLine("XML Serialized: " + xmlData); // Запись XML в файл File.WriteAllText("countrys.xml", xmlData); // Чтение XML из файла и десериализация string xmlFromFile = File.ReadAllText("countrys.xml"); var deserializedPeopleXml = xmlSerializer.Deserialize<List<Country>>(xmlFromFile); Console.WriteLine("Deserialized from XML:"); foreach (var countrie in deserializedPeopleXml) { Console.WriteLine($"{countrie.Name}, {countrie.Rank}"); } string filePath = "countrys.xml"; using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) using (var writer = new StreamWriter(fs, new System.Text.UnicodeEncoding())) { writer.Write("<?xml version=\"1.0\" encoding=\"UTF-16\"?>"); writer.Write("<Countrys>"); writer.Write("<Country>"); writer.Write("<Name>Russia</Name>"); writer.Write("<Rank>10</Rank>"); writer.Write("<Password>secret_password</Password>"); writer.Write("</Country>"); writer.Write("</Countrys>"); } XDocument xdoc; try { xdoc = XDocument.Load(filePath); var countryNames = xdoc.XPathSelectElements("//Country/Name"); Console.WriteLine("Все названия стран:"); foreach (var name in countryNames) { Console.WriteLine(name.Value); } var countriesWithHighRank = xdoc.XPathSelectElements("//Country[Rank > 5]/Name"); Console.WriteLine("\nСтраны с рангом больше 5:"); foreach (var name in countriesWithHighRank) { Console.WriteLine(name.Value); } } catch (XmlException ex) { Console.WriteLine("Ошибка при загрузке XML: " + ex.Message); } var states = new XElement("Countrys", new XElement("Country", new XElement("Name", "Russia"), new XElement("Rank", 10), new XElement("Password", "secret_password")), new XElement("Country", new XElement("Name", "Belarus"), new XElement("Rank", 20), new XElement("Password", "another_password")), new XElement("Country", new XElement("Name", "China"), new XElement("Age", 5), new XElement("Password", "mypassword") ) ); states.Save("countrys.xml"); var names = from state in states.Elements("Country") select state.Element("Name").Value; Console.WriteLine("\nAll names:"); foreach (var name in names) { Console.WriteLine(name); } var worserThan15 = from state in states.Elements("Country") let rank = (int)state.Element("Rank") where rank > 15 select state.Element("Name").Value; Console.WriteLine("Rank worser than 15:"); foreach (var name in worserThan15) { Console.WriteLine(name); } }
}
Чем подробнее объяснишь, тем лучше
Ниже приведён подробный разбор кода и пояснение к его работе, разбитое на логические блоки, чтобы вы могли понимать, что и где происходит.
Нужно продемонстрировать сериализацию и десериализацию объектов в разные форматы:
При этом один из членов класса должен быть закрыт от сериализации (запрещено сериализовать). В коде это поле Password
с атрибутом [NonSerialized]
.
Все сериализаторы (JSON, XML, SOAP и т.д.) должны реализовывать единый интерфейс (в коде это интерфейс ISerializer
), чтобы можно было легко подключать или заменять сериализаторы, не меняя основного кода.
Нужно выполнить:
В коде есть все необходимые части:
Country
с запрещённым к сериализации полем,CountryList
для удобства сериализации массива,JsonSerializer
, XmlSerializer
, SoapSerializer
(все имплементируют ISerializer
),Main
, где демонстрируется работа с этими сериализаторами и чтение/запись в файлы,ISerializer
csharppublic interface ISerializer { string Serialize(object obj); T Deserialize<T>(string data); }
object obj
) и возвращает строку (string
), где будет содержаться результат сериализации (например, JSON-строка, XML-строка, SOAP-строка).T
.Благодаря такому интерфейсу мы можем, не меняя основного кода, подставлять любой класс-сериализатор. Все классы, реализующие ISerializer
, будут иметь одинаковые методы (Serialize и Deserialize), но внутри них логика будет отличаться (JSON, XML, SOAP).
Country
с запрещённым полемcsharp[Serializable] public class Country { public string Name { get; set; } public int Rank { get; set; } [NonSerialized] // Запрет на сериализацию этого поля public string Password; public Country() { } // Пустой конструктор для десериализации public Country(string name, int number, string password) { Name = name; Rank = number; Password = password; } }
[Serializable]
говорит, что этот класс может участвовать в механизмe сериализации (в частности, используется SOAP- и Binary-сериализациями .NET).Password
помечено атрибутом [NonSerialized]
.XmlSerializer
и Json.NET
) чуть другие механизмы, но в нашем случае XmlSerializer
и JsonConvert
тоже не запишут это поле, поскольку для атрибута NonSerialized
(и при работе с XmlSerializer
, если поле не имеет get; set;
) оно чаще всего будет пропущено.[JsonIgnore]
, [XmlIgnore]
и т.д.CountryList
csharp[Serializable] public class CountryList { public Country[] Countries { get; set; } // Использование массива вместо List public CountryList() { Countries = new Country[0]; } public static CountryList FromList(List<Country> countryList) { return new CountryList { Countries = countryList.ToArray() }; } public List<Country> ToList() { return new List<Country>(Countries); } }
List<Country>
).List<T>
.FromList
и ToList
помогают «конвертировать»:
List<Country>
в Country[]
(для удобства сериализации),Country[]
в List<Country>
(для удобства использования в коде).Json.NET
)csharppublic class JsonSerializer : ISerializer { public string Serialize(object obj) => JsonConvert.SerializeObject(obj); public T Deserialize<T>(string data) => JsonConvert.DeserializeObject<T>(data); }
JsonConvert.SerializeObject(obj)
, который превращает объект в JSON-строку.JsonConvert.DeserializeObject<T>(data)
, который из JSON-строки создаёт обратно объект указанного типа T
.Обратите внимание, что мы используем библиотеку Newtonsoft.Json
(подключается обычно через NuGet).
System.Xml.Serialization.XmlSerializer
)csharppublic class XmlSerializer : ISerializer { public string Serialize(object obj) { var xmlSerializer = new System.Xml.Serialization.XmlSerializer(obj.GetType()); var stringWriter = new StringWriter(); xmlSerializer.Serialize(stringWriter, obj); return stringWriter.ToString(); } public T Deserialize<T>(string data) { var xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T)); var stringReader = new StringReader(data); return (T)xmlSerializer.Deserialize(stringReader); } }
В Serialize:
XmlSerializer
(класс из System.Xml.Serialization
), которому передаётся тип сериализуемого объекта.StringWriter
, куда XmlSerializer
запишет результат в XML-формате.В Deserialize:
XmlSerializer
на тип T
.StringReader
для чтения XML-строки.Deserialize
, который из XML-строки «собирает» объект заданного типа.csharppublic class SoapSerializer : ISerializer { public string Serialize(object obj) { using (var memoryStream = new MemoryStream()) { IFormatter formatter = new SoapFormatter(); formatter.Serialize(memoryStream, obj); return Convert.ToBase64String(memoryStream.ToArray()); } } public T Deserialize<T>(string data) { var bytes = Convert.FromBase64String(data); using (var memoryStream = new MemoryStream(bytes)) { IFormatter formatter = new SoapFormatter(); return (T)formatter.Deserialize(memoryStream); } } }
SoapFormatter
.MemoryStream
.SoapFormatter
.memoryStream
.Convert.ToBase64String(...)
. Это нужно, чтобы удобно было выводить/хранить сериализованные данные в виде «текстовой строки».Convert.FromBase64String
, чтобы получить исходный массив байтов.MemoryStream
.SoapFormatter
метод Deserialize
и приводим результат к типу T
.Main
и основная демонстрацияcsharppublic static void Main() { // Создаём список стран var countryList = new List<Country> { new Country("Russia", 10, "secret_password"), new Country("Belarus", 20, "another_password"), new Country("China", 5, "mypassword") }; // Преобразуем список в класс-обертку var wrappedCountryList = CountryList.FromList(countryList); // Используем SOAP сериализатор ISerializer soapSerializer = new SoapSerializer(); string soapData = soapSerializer.Serialize(wrappedCountryList); Console.WriteLine("SOAP Serialized: " + soapData); // Запись SOAP в файл File.WriteAllText("countrys.soap", soapData); // Десериализация коллекции var deserializedCountryList = soapSerializer.Deserialize<CountryList>(soapData); Console.WriteLine("Deserialized Countries from SOAP:"); // Печатаем результат десериализации foreach (var country in deserializedCountryList.ToList()) { Console.WriteLine($"Name: {country.Name}, Rank: {country.Rank}, Password: {country.Password}"); } ... }
Создаётся список List<Country>
с тремя странами. Каждая страна имеет поля:
Name
("Russia", "Belarus", "China"),Rank
(10, 20, 5),Password
("secret_password", "another_password", "mypassword").Оборачиваем список countryList
в объект CountryList
(метод FromList
), поскольку для SOAP-сериализации (и вообще для части сериализаторов) удобнее работать с классом, а не напрямую со списком.
Soap-сериализация:
SoapSerializer
.soapSerializer.Serialize(wrappedCountryList)
, результат кладём в строку soapData
.countrys.soap
.Soap-десериализация:
soapData
(либо могли бы взять из файла).deserializedCountryList = soapSerializer.Deserialize<CountryList>(soapData)
.CountryList
.deserializedCountryList.ToList()
превращаем его в List<Country>
.Password
(если SOAP учёл атрибут [NonSerialized]
) будет null
или пустым (зависит от поведения при десериализации).Далее аналогично идёт демонстрация JSON и XML сериализации:
csharp// Используем JSON сериализатор ISerializer jsonSerializer = new JsonSerializer(); string jsonData = jsonSerializer.Serialize(countryList); Console.WriteLine("JSON Serialized: " + jsonData); // Запись JSON в файл File.WriteAllText("countrys.json", jsonData); // Чтение JSON из файла и десериализация string jsonFromFile = File.ReadAllText("countrys.json"); var deserializedPeopleJson = jsonSerializer.Deserialize<List<Country>>(jsonFromFile); Console.WriteLine("Deserialized from JSON:"); foreach (var countrie in deserializedPeopleJson) { Console.WriteLine($"{countrie.Name}, {countrie.Rank}"); }
wrappedCountryList
, а просто List<Country>
. С Json.NET
можно делать и так, никаких проблем.countrys.json
, потом оттуда читаем.csharp// Используем XML сериализатор ISerializer xmlSerializer = new XmlSerializer(); string xmlData = xmlSerializer.Serialize(countryList); Console.WriteLine("XML Serialized: " + xmlData); // Запись XML в файл File.WriteAllText("countrys.xml", xmlData); // Чтение XML из файла и десериализация string xmlFromFile = File.ReadAllText("countrys.xml"); var deserializedPeopleXml = xmlSerializer.Deserialize<List<Country>>(xmlFromFile); Console.WriteLine("Deserialized from XML:"); foreach (var countrie in deserializedPeopleXml) { Console.WriteLine($"{countrie.Name}, {countrie.Rank}"); }
XmlSerializer
.countrys.xml
, чтение оттуда, вывод.После того как мы продемонстрировали сериализацию/десериализацию, идёт пример вставки XML вручную и работы с ним через XPath.
csharpstring filePath = "countrys.xml"; using (var fs = new FileStream(filePath, FileMode.Create, FileAccess.Write)) using (var writer = new StreamWriter(fs, new System.Text.UnicodeEncoding())) { writer.Write("<?xml version=\"1.0\" encoding=\"UTF-16\"?>"); writer.Write("<Countrys>"); writer.Write("<Country>"); writer.Write("<Name>Russia</Name>"); writer.Write("<Rank>10</Rank>"); writer.Write("<Password>secret_password</Password>"); writer.Write("</Country>"); writer.Write("</Countrys>"); }
Здесь код принудительно записывает XML в файл countrys.xml
, заменяя то, что было до этого.
Получается следующая структура:
xml<?xml version="1.0" encoding="UTF-16"?> <Countrys> <Country> <Name>Russia</Name> <Rank>10</Rank> <Password>secret_password</Password> </Country> </Countrys>
Далее идёт код:
csharpXDocument xdoc; try { xdoc = XDocument.Load(filePath); var countryNames = xdoc.XPathSelectElements("//Country/Name"); Console.WriteLine("Все названия стран:"); foreach (var name in countryNames) { Console.WriteLine(name.Value); } var countriesWithHighRank = xdoc.XPathSelectElements("//Country[Rank > 5]/Name"); Console.WriteLine("\nСтраны с рангом больше 5:"); foreach (var name in countriesWithHighRank) { Console.WriteLine(name.Value); } } catch (XmlException ex) { Console.WriteLine("Ошибка при загрузке XML: " + ex.Message); }
XDocument.Load(filePath)
– загружает XML-документ из файла.XPathSelectElements("//Country/Name")
– находит все элементы <Name>
внутри <Country>
в любом месте XML (путь //Country/Name
).name.Value
для каждого найденного элемента – получаем «Russia».XPathSelectElements("//Country[Rank > 5]/Name")
– находит все <Country>
с условием, что значение <Rank>
внутри них больше 5, и у этих элементов берёт <Name>
.
Rank = 10
для Russia, это удовлетворяет Rank > 5
.Далее идёт код:
csharpvar states = new XElement("Countrys", new XElement("Country", new XElement("Name", "Russia"), new XElement("Rank", 10), new XElement("Password", "secret_password")), new XElement("Country", new XElement("Name", "Belarus"), new XElement("Rank", 20), new XElement("Password", "another_password")), new XElement("Country", new XElement("Name", "China"), new XElement("Age", 5), new XElement("Password", "mypassword") ) ); states.Save("countrys.xml");
XElement
), которая будет иметь корень <Countrys>
и внутри три <Country>
.China
для демонстрации вставлено поле <Age>
вместо <Rank>
(скорее всего, чтобы показать, что можно произвольно изменять структуру).states
в файл countrys.xml
.csharpvar names = from state in states.Elements("Country") select state.Element("Name").Value; Console.WriteLine("\nAll names:"); foreach (var name in names) { Console.WriteLine(name); } var worserThan15 = from state in states.Elements("Country") let rank = (int)state.Element("Rank") where rank > 15 select state.Element("Name").Value; Console.WriteLine("Rank worser than 15:"); foreach (var name in worserThan15) { Console.WriteLine(name); }
from state in states.Elements("Country")
– берём все дочерние элементы <Country>
из корневого <Countrys>
.select state.Element("Name").Value
– берём значение <Name>
для каждой страны.
rank = (int)state.Element("Rank")
.China
вместо <Rank>
стоит <Age>
, поэтому для него state.Element("Rank")
вернёт null
, и при попытке (int)null
будет исключение (в реальном коде нужно обрабатывать или проверять, что элемент не null). Но тут демонстрационный пример.where rank > 15
— выбираем только те, у кого Rank
больше 15. По структуре XML это будет у «Belarus» (Rank = 20
).ISerializer
позволяет подменять разные реализации сериализаторов (JSON, XML, SOAP) без изменения остального кода.Country
– демонстрирует атрибут [NonSerialized]
на поле Password
, которое (в классической сериализации .NET, например SOAP или Binary) не будет сохранено.CountryList
– служит удобной обёрткой для массивов (или списков), чтобы проще передавать их в методы сериализаторов (особенно для SOAP- и XmlSerializer
).Main
:
Country
.countrys.soap
),countrys.json
),countrys.xml
).Password
при десериализации исчезает (или null), тем самым показав запрет на сериализацию.XDocument
и XPath:
//Country/Name
или //Country[Rank > 5]/Name
.XElement
) и выполнять к нему LINQ-запросы, похожие на SQL по синтаксису (from ... select ...).Это даёт полное решение по части задания: есть сериализация в несколько форматов, есть пример работы с XML через XPath и LINQ to XML/JSON, а также продемонстрирован механизм исключения поля (Password
) из сериализации.
SoapSerializer
, только использовать BinaryFormatter
(но в более новых версиях .NET она уже не рекомендуется).XmlSerializer
и/или Json.NET
можно было бы дополнительно использовать [XmlIgnore]
и [JsonIgnore]
.Password
не является свойством с { get; set; }
, а лишь обычным public string
, оно часто уже не будет сериализовываться XmlSerializer’ом. Но тут могут быть тонкости.SOAP
поле Password
точно не будет сериализовано благодаря [NonSerialized]
.Таким образом, код выполняет все основные пункты задания:
CountryList
и Country
).Password
показан через атрибут [NonSerialized]
.JsonConvert
),XmlSerializer
).List<Country>
) сериализуются и десериализуются, сохраняются в файлы.