Содержание
В прошлой части мы, в общих чертах, разобрались с тем, что из себя представляет сериализация JSON в C# и научились настраивать опции сериализации/десериализации JSON в C#. Сегодня мы рассмотрим вопросы, связанные с сериализацией JSON производных классов (классов-потомков).
Проблема полиморфизма при сериализации JSON в C#
Итак, вернемся к нашему классу Person, который выглядит вот так:
public class Person { public string Name { get; set; } [JsonPropertyName("PersonFamily")] public string Surname { get; set; } public int age; [JsonIgnore] public int Age //теперь свойство Age не будет сериализоваться { get => age; set { if ((value < 0) || (value > 100)) throw new Exception("Возраст должен находится в интервале от 0 до 100 лет"); else age = value; } } public DateTime Birthday { get; set; } public Person(string name) { Name = name; } public Person() { } }
здесь мы с помощью атрибутов настроили сериализацию, а также объявили конструктор без параметров, чтобы объект мог спокойно десериализоваться из JSON. Теперь попробуем создать производный класс, например, описывающий аккаунт пользователя:
public class Account : Person { public string Login { get; set; } public string Password { get; set; } }
Если мы попробуем сериализовать объект такого класса, то никаких проблем мы не будем наблюдать — настройки сериализации свойств сохранятся и мы увидим ровно то, что и ожидаем увидеть:
Account account = new Account() { Name = "Вася", Surname = "Пупкин", Age = 38, Birthday = new DateTime(1983, 01, 16), Login = "Uasya", Password = "qwerty" }; JsonSerializerOptions options = new JsonSerializerOptions() { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; string personJson = JsonSerializer.Serialize(account, options); Console.WriteLine(personJson);
{
"Login": "Uasya",
"Password": "qwerty",
"Name": "Вася",
"PersonFamily": "Пупкин",
"Birthday": "1983-01-16T00:00:00"
}
JsonSerializer
поместил свойства производного класса (Account
) перед свойствами базового класса, но результат ожидаемый. Проблемы с сериализацией производных классов в JSON начинаются в том случае, если мы попробуем применить при работ с нашим новым классом полиморфизм. Например, попробуем сериализовать вот такой объект:
Person account = new Account() //фактически, account - это объект типа Account { Name = "Вася", Surname = "Пупкин", Age = 38, Birthday = new DateTime(1983, 01, 16), Login = "Uasya", Password = "qwerty" };
Результат сериализации будет такой:
{
"Name": "Вася",
"PersonFamily": "Пупкин",
"Birthday": "1983-01-16T00:00:00"
}
Сериализовались только свойства базового класса, несмотря на то, что переменная account
— это, фактически, объект типа Account
. Такое поведение сериализатора JsonSerializer
предназначено для предотвращения случайного доступа к данным в производном типе, созданном во время выполнения. Чтобы сериализовать объект производного класса мы можем воспользоваться следующими подходами.
Полиморфная сериализация JSON
Явно указываем тип сериализуемого объекта
Чтобы сериализовать в JSON объект производного типа, можно воспользоваться одной из перегрузок метода Serialize
, явно указав тип сериализуемого объекта:
string accountJson = JsonSerializer.Serialize(account, typeof(Account), //явно указали какой тип содержится в account options);
в этом случае, результат сериализации будет полным, т.е. в строку JSON попадут все свойства класса Account
.
Указываем тип сериализуемого объекта как object
string account2Json = JsonSerializer.Serialize<object>(account, options);
в этом случае, как и в предыдущем, строка JSON будет содержать все сериализуемые свойства производного класса:
{
"Login": "Uasya",
"Password": "qwerty",
"Name": "Вася",
"PersonFamily": "Пупкин",
"Birthday": "1983-01-16T00:00:00"
}
Итого
В случае использования полиморфизма возможны проблемы при сериализации JSON производных классов. Для того, чтобы полностью сериализовать полиморфный объект в JSON необходимо либо явно указывать тип сериализуемого объекта, либо использовать самый общий тип — object
.