Как сериализовать свойства производных классов с помощью System.Text.JsonHow to serialize properties of derived classes with System.Text.Json

Из этой статьи вы узнаете, как сериализовать свойства производных классов с помощью пространства имен System.Text.Json.In this article, you will learn how to serialize properties of derived classes with the System.Text.Json namespace.

Сериализация свойств производных классовSerialize properties of derived classes

Сериализация иерархии полиморфных типов не поддерживается.Serialization of a polymorphic type hierarchy is not supported. Например, если свойство определено как интерфейс или абстрактный класс, сериализуются только свойства, определенные в интерфейсе или абстрактном классе, даже если тип среды выполнения имеет дополнительные свойства.For example, if a property is defined as an interface or an abstract class, only the properties defined on the interface or abstract class are serialized, even if the runtime type has additional properties. В этом разделе описаны исключения из этого поведения.The exceptions to this behavior are explained in this section.

Например, предположим, что у вас есть класс WeatherForecast и производный класс WeatherForecastDerived:For example, suppose you have a WeatherForecast class and a derived class WeatherForecastDerived:

public class WeatherForecast
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}
public class WeatherForecastDerived : WeatherForecast
{
    public int WindSpeed { get; set; }
}

Предположим также, что аргумент типа метода Serialize во время компиляции WeatherForecast:And suppose the type argument of the Serialize method at compile time is WeatherForecast:

var options = new JsonSerializerOptions
{
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecast>(weatherForecast, options);

В этом сценарии свойство WindSpeed не сериализуется, даже если объект weatherForecast фактически является объектом WeatherForecastDerived.In this scenario, the WindSpeed property is not serialized even if the weatherForecast object is actually a WeatherForecastDerived object. Сериализуются только свойства базового класса:Only the base class properties are serialized:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot"
}

Это поведение предназначено для предотвращения случайного доступа к данным в производном типе, созданном во время выполнения.This behavior is intended to help prevent accidental exposure of data in a derived runtime-created type.

Чтобы сериализовать свойства производного типа в предыдущем примере, используйте один из следующих подходов:To serialize the properties of the derived type in the preceding example, use one of the following approaches:

  • Вызовите перегрузку Serialize, которая позволяет указать тип во время выполнения:Call an overload of Serialize that lets you specify the type at run time:

    options = new JsonSerializerOptions
    {
        WriteIndented = true
    };
    jsonString = JsonSerializer.Serialize(weatherForecast, weatherForecast.GetType(), options);
    
  • Объявите объект, который должен быть сериализован как object.Declare the object to be serialized as object.

    options = new JsonSerializerOptions
    {
        WriteIndented = true
    };
    jsonString = JsonSerializer.Serialize<object>(weatherForecast, options);
    

В предыдущем примере сценария оба подхода приводят к тому, что WindSpeed свойство включается в выходные данные JSON:In the preceding example scenario, both approaches cause the WindSpeed property to be included in the JSON output:

{
  "WindSpeed": 35,
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot"
}

Важно!

Эти подходы обеспечивают одноэлементную сериализацию только для сериализации корневого объекта, а не для его свойств.These approaches provide polymorphic serialization only for the root object to be serialized, not for properties of that root object.

Вы можете выполнить полиморфную сериализацию для объектов более низкого уровня, если вы определите их как тип object.You can get polymorphic serialization for lower-level objects if you define them as type object. Например, предположим, что у класса WeatherForecast есть свойство PreviousForecast, которое может быть определено как тип WeatherForecast или object:For example, suppose your WeatherForecast class has a property named PreviousForecast that can be defined as type WeatherForecast or object:

public class WeatherForecastWithPrevious
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    public WeatherForecast PreviousForecast { get; set; }
}
public class WeatherForecastWithPreviousAsObject
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
    public object PreviousForecast { get; set; }
}

Если свойство PreviousForecast содержит экземпляр WeatherForecastDerived:If the PreviousForecast property contains an instance of WeatherForecastDerived:

  • Выходные данные JSON из сериализации WeatherForecastWithPrevious не содержат WindSpeed.The JSON output from serializing WeatherForecastWithPrevious doesn't include WindSpeed.
  • Выходные данные JSON из сериализации WeatherForecastWithPreviousAsObject включают в себя WindSpeed.The JSON output from serializing WeatherForecastWithPreviousAsObject includes WindSpeed.

Чтобы сериализовать WeatherForecastWithPreviousAsObject, не нужно вызывать Serialize<object> или GetType, потому что корневой объект не является объектом, который может иметь производный тип.To serialize WeatherForecastWithPreviousAsObject, it isn't necessary to call Serialize<object> or GetType because the root object isn't the one that may be of a derived type. В следующем примере кода не вызывается Serialize<object> или GetType:The following code example doesn't call Serialize<object> or GetType:

options = new JsonSerializerOptions
{
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject, options);

Приведенный выше код правильно сериализует WeatherForecastWithPreviousAsObject:The preceding code correctly serializes WeatherForecastWithPreviousAsObject:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "PreviousForecast": {
    "WindSpeed": 35,
    "Date": "2019-08-01T00:00:00-07:00",
    "TemperatureCelsius": 25,
    "Summary": "Hot"
  }
}

Аналогичный подход к определению свойств object работает с интерфейсами.The same approach of defining properties as object works with interfaces. Предположим, что у вас есть следующий интерфейс и реализация и вы хотите сериализовать класс со свойствами, содержащими экземпляры реализации:Suppose you have the following interface and implementation, and you want to serialize a class with properties that contain implementation instances:

using System;

namespace SystemTextJsonSamples
{
    public interface IForecast
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string Summary { get; set; }
    }

    public class Forecast : IForecast
    {
        public DateTimeOffset Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string Summary { get; set; }
        public int WindSpeed { get; set; }
    }

    public class Forecasts
    {
        public IForecast Monday { get; set; }
        public object Tuesday { get; set; }
    }
}

Когда вы сериализуете экземпляр Forecasts, только Tuesday отображает свойство WindSpeed, так как Tuesday определяется как object:When you serialize an instance of Forecasts, only Tuesday shows the WindSpeed property, because Tuesday is defined as object:

var forecasts = new Forecasts
{
    Monday = new Forecast
    {
        Date = DateTime.Parse("2020-01-06"),
        TemperatureCelsius = 10,
        Summary = "Cool",
        WindSpeed = 8
    },
    Tuesday = new Forecast
    {
        Date = DateTime.Parse("2020-01-07"),
        TemperatureCelsius = 11,
        Summary = "Rainy",
        WindSpeed = 10
    }
};

options = new JsonSerializerOptions
{
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(forecasts, options);

В следующем примере показан код JSON, который является результатом предыдущего кода:The following example shows the JSON that results from the preceding code:

{
  "Monday": {
    "Date": "2020-01-06T00:00:00-08:00",
    "TemperatureCelsius": 10,
    "Summary": "Cool"
  },
  "Tuesday": {
    "Date": "2020-01-07T00:00:00-08:00",
    "TemperatureCelsius": 11,
    "Summary": "Rainy",
    "WindSpeed": 10
  }
}

Дополнительные сведения о полиморфной сериализации и десериализации см. в статье Миграция из Newtonsoft.Json в System.Text.Json.For more information about polymorphic serialization, and for information about deserialization, see How to migrate from Newtonsoft.Json to System.Text.Json.

См. также разделSee also