Работа с JSON в C#: сериализация производных классов

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

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии