Добавлен Msey,
опубликован
В этой статье будет подробно разобрана сериализация/десериализация объектов, ее предназначение, форматы и случаи, где какой формат сериализации использовать.
В наше время нередко приходится сталкиваться с такими ситуациями, когда на запоминающем устройстве нужно сохранить необходимую информацию, где оная может принимать вид некоторых структур данных. Структуры данных могут быть представлены как в виде простых объектов с парой параметров, так и сложных в виде многочисленных иерархий объектов. При становлении вопроса о сохранении этих данных, у вас либо какая-то агрессия и зубы скрипят, либо вы вспоминаете то, что здесь было изложено и, по выполнении задачи, продолжаете радоваться жизни.
С этой проблемой призван справится механизм сериализации, где сериализация еть процесс преобразования какой-либо сущности в поток байтов. После преобразования мы можем этот поток байтов или записать на диск в необходимом формате или сохранить его временно в памяти. А при необходимости можно выполнить обратный процесс - десериализацию, то есть, получить из потока байтов ранее сохраненный объект и привести в изначальный вид.
Перечислю несколько распространенных форматов сериализации/десериализации, где каждый из перечисленных имеет свои преимущества.
Форматы и проведенный бенчмарк:
- Xml-сериализация
- Json-сериализация
| + исходный файл меньше весит при больших и малых объемах данных
Идеально подходит для кратковременной сериализации объектов Unity.
- Бинарная-сериализация
| + поддерживает больше типов для де/сериализации
Подходит к долговременной сериализации объектов Unity, где подразумевается сокрытие данных от "взломщиков".
| большим объемом считается количество более 1500 объектов
| малым объемом считается количество менее 500 объектов
| малым объемом считается количество менее 500 объектов
XML сериализация
XML сериализация сериализует только публичные поля и свойства
XML сериализация должна должна на этапе компиляции располагать информацией о типах, которые сериализует
Сериализуемые объекты должны иметь беспараметрический конструктор
Свойства с модификатором readonly не сериализуются
XML сериализация должна должна на этапе компиляции располагать информацией о типах, которые сериализует
Сериализуемые объекты должны иметь беспараметрический конструктор
Свойства с модификатором readonly не сериализуются
Для начала напишем класс-шаблон для сведений об игровом состоянии. Выглядеть он будет следующим образом:
public class GameState
{
public int Money { get; set; }
public int Lives { get; set; }
}
И метод для сериализации наших данных.
GameState state = new GameState() // создаем объект с данными, базируясь на классе-шаблоне
{
Money = 1488,
Lives = 228
};
XmlSerializer serializer = new XmlSerializer(typeof(GameState)); // создаем сериализатор и сообщаем ему о том, какой тип сериализуем
using (TextWriter writer = new StreamWriter(@"C:\Users\Msey\Desktop\GameState.xml")) // если вкратце, то здесь мы создаем модуль, позволяющий записывать символы по указанной директории
{
serializer.Serialize(writer, state); // сериализуем данные
}
Выходные данные будут выглядеть следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<GameState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Money>1488</Money>
<Lives>228</Lives>
</GameState>
Теперь отредактируем наш класс-шаблон так, чтобы убедиться, что приватные поля не сериализуются и что с непараметрическим конструктором все работает в полной мере.
public class GameState
{
public int money;
public int lives;
private int weed;
public GameState()
{
money = 1111;
lives = 2222;
weed = 3333; // не будет сериализоваться, тк поле weed с модификатором доступа private
}
}
Результат очевиден:
<?xml version="1.0" encoding="utf-8"?>
<GameState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<money>1111</money>
<lives>2222</lives>
</GameState>
Если мы заменим пустой конструктор на конструктор с параметрами, то в процессе выполнения программы мы получим ошибку выполнения на этапе создания сериализатора:
public GameState(int i =1111, int j =2222) // так нельзя - конструктор с параметрами
{
money = i;
lives = j;
}
public GameState(int i , int j) // так тоже нельзя - конструктор продолжает быть параметрическим (Ваш кэп ©)
{
money = i;
lives = j;
}
Рассмотрим распространенные атрибуты XML-сериализации:
- [XmlElement]: поле будет сериализовано в качестве элемента
- [XmlAttribute]: поле будет сериализовано в качестве атрибута
- [XmlIgnore]: поле будет пропущено во время сериализации
- [XmlRoot]: задает корневой элемент при сериализации
Рассмотрим атрибут XmlElement:
public class GameState
{
[XmlElement("no_money")]
public int money; // независимо от того, как называется поле, атрибут XmlElement указывает, что представляет элемент строкой ниже и сериализует/десериализует его под именем, указанном в этом атрибуте
[XmlElement("no_lives")]
public int lives;
public GameState()
{
money = 1111;
lives = 2222;
}
}
Результат:
<?xml version="1.0" encoding="utf-8"?>
<GameState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<no_money>1111</no_money>
<no_lives>2222</no_lives>
</GameState>
Также атрибут [XmlElement("no_money")] можно записать в виде [XmlElement(ElementName ="no_money")]. Отличие заключается в том, что в атрибуте [XmlElement(ElementName ="no_money")] можно записать несколько параметров:
[XmlElement(ElementName ="no_money", Namespace = "nmspc")]
Здесь мы добавили пространство имен, и тогда данный элемент в файле принимает следующий вид:
<no_money xmlns="nmspc">1111</no_money>
Теперь обратим внимание на поведение при аттрибуте XmlAttribute:
public class GameState
{
[XmlAttribute("MoneyAttribute")]
public int money;
[XmlAttribute("LivesAttribute")]
public int lives;
public GameState()
{
money = 2222;
lives = 2222;
}
}
Результат:
<?xml version="1.0" encoding="utf-8"?>
<GameState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" MoneyAttribute="2222" LivesAttribute="2222" />
Аттрибут XmlIgnore позволяет игнорировать поле во время сериализации. Заменим класс:
public class GameState
{
[XmlIgnore]
public int money;
[XmlIgnore]
public int lives;
public GameState()
{
money = 1111;
lives = 2222;
}
}
И наблюдаем результат:
<?xml version="1.0" encoding="utf-8"?>
<GameState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
В сгенерированном XML файле отсутствуют элементы.
Атрибут XmlRoot применяется в качестве указания корневого каталога его содержимого. Корневыми элементами могут быть структуры, перечисления, классы и интерфейсы.
[XmlRoot("Root_GameState")]
public class GameState
{
public int money;
public int lives;
public GameState()
{
money = 1111;
lives = 2222;
}
}
Результат:
<?xml version="1.0" encoding="utf-8"?>
<Root_GameState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<money>1111</money>
<lives>2222</lives>
</Root_GameState>
Десериализация для класса GameState не слишком сильно отличается от сериализации:
GameState state;
XmlSerializer deserializer = new XmlSerializer(typeof(GameState));
using (TextReader reader = new StreamReader(@"C:\Users\Msey\Desktop\GameState.xml"))
{
state = (GameState) deserializer.Deserialize(reader);
}
Json сериализация
Сериализация в json имеет схожий с XML процесс сериализации:
GameState state = new GameState();
JsonSerializer serializer = new JsonSerializer();
using (StreamWriter sw = new StreamWriter(@"C:\Users\Msey\Desktop\GameState.json"))
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, state);
}
Перечислю, пожалуй, основные атрибуты для сериализации в json, которые вам могут пригодиться в дальнейшем:
- [JsonObjectAttribute] - атрибут, который используется для задания поведения класса при сериализации
- [JsonPropertyAttribute] - атрибут, который используется для задания поведения свойств и полей при сериализации
- [JsonIgnore] - атрибут, позволяющий игнорировать поле или свойство при сериализации
Десериализуем теперь json обратно в объект:
GameState state;
JsonSerializer serializer = new JsonSerializer();
using (StreamReader sw = new StreamReader(@"C:\Users\Msey\Desktop\GameState.json"))
using (JsonReader writer = new JsonTextReader(sw))
{
state = (GameState) serializer.Deserialize(writer);
}
Бинарная сериализация
Процесс бинарной сериализации выглядит так:
GameState state = new GameState();
BinaryFormatter formatter = new BinaryFormatter();
using (FileStream stream = new FileStream(@"C:\Users\Msey\Desktop\GameState.dat", FileMode.Create))
{
formatter.Serialize(stream, state);
}
Однако стоит заметить, что конструктор BinaryFormatter не принимает тип сериализуемого объекта, а это значит, что на этапе компиляции formatter не будет ничего знать о нашем классе (типе) с игровым состоянием и в процессе выполнения выдаст ошибку, поэтому добавим к нему атрибут [Serializable]:
[Serializable]
public class GameState
{
public int money;
public int lives;
public GameState()
{
money = 1111;
lives = 2222;
}
}
В случае с десериализацией можно пренебречь атрибутом [Serializable], однако я рекомендую этот атрибут использовать всегда для сериализуемых объектов как для читаемости, так и "кросссериализуемости".
Процесс десериализации:
Процесс десериализации:
GameState state;
BinaryFormatter formatter = new BinaryFormatter();
using (FileStream stream = new FileStream("C:\Users\Msey\Desktop\GameState.dat", FileMode.Open))
{
state = (GameState)formatter.Deserialize(stream);
}
Статья будет дополняться.
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Отредактирован Msey
Отредактирован Doc
money 999.99
lives 12
money 999.99 Since 1.2
lives 12
Отредактирован Doc