Содержание
В прошлой части мы, в общих чертах, разобрались с тем, что из себя представляет сериализация 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.