How to serialize properties of derived classes with 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.
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 WeatherForecast
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
End Class
public class WeatherForecastDerived : WeatherForecast
{
public int WindSpeed { get; set; }
}
Public Class WeatherForecastDerived
Inherits WeatherForecast
Public Property WindSpeed As Integer
End Class
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);
Dim options As JsonSerializerOptions = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast1, options)
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:
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);
options = New JsonSerializerOptions With { .WriteIndented = True } jsonString = JsonSerializer.Serialize(weatherForecast1, weatherForecast1.[GetType](), options)
Declare the object to be serialized as
object
.options = new JsonSerializerOptions { WriteIndented = true }; jsonString = JsonSerializer.Serialize<object>(weatherForecast, options);
options = New JsonSerializerOptions With { .WriteIndented = True } jsonString = JsonSerializer.Serialize(Of Object)(weatherForecast1, options)
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"
}
Important
These approaches provide polymorphic serialization only for the root object to be serialized, not for properties of that root object.
You can get polymorphic serialization for lower-level objects if you define them as type 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 WeatherForecastWithPrevious
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
Public Property PreviousForecast As WeatherForecast
End Class
public class WeatherForecastWithPreviousAsObject
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public object? PreviousForecast { get; set; }
}
Public Class WeatherForecastWithPreviousAsObject
Public Property [Date] As DateTimeOffset
Public Property TemperatureCelsius As Integer
Public Property Summary As String
Public Property PreviousForecast As Object
End Class
If the PreviousForecast
property contains an instance of WeatherForecastDerived
:
- The JSON output from serializing
WeatherForecastWithPrevious
doesn't includeWindSpeed
. - The JSON output from serializing
WeatherForecastWithPreviousAsObject
includesWindSpeed
.
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. The following code example doesn't call Serialize<object>
or GetType
:
options = new JsonSerializerOptions
{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject, options);
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecastWithPreviousAsObject1, options)
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"
}
}
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:
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; }
}
}
Namespace SystemTextJsonSamples
Public Interface IForecast
Property [Date] As DateTimeOffset
Property TemperatureCelsius As Integer
Property Summary As String
End Interface
Public Class Forecast
Implements IForecast
Public Property [Date] As DateTimeOffset Implements IForecast.[Date]
Public Property TemperatureCelsius As Integer Implements IForecast.TemperatureCelsius
Public Property Summary As String Implements IForecast.Summary
Public Property WindSpeed As Integer
End Class
Public Class Forecasts
Public Property Monday As IForecast
Public Property Tuesday As Object
End Class
End Namespace
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);
Dim forecasts1 As New Forecasts With {
.Monday = New Forecast With {
.[Date] = Date.Parse("2020-01-06"),
.TemperatureCelsius = 10,
.Summary = "Cool",
.WindSpeed = 8
},
.Tuesday = New Forecast With {
.[Date] = Date.Parse("2020-01-07"),
.TemperatureCelsius = 11,
.Summary = "Rainy",
.WindSpeed = 10
}
}
options = New JsonSerializerOptions With {
.WriteIndented = True
}
jsonString = JsonSerializer.Serialize(forecasts1, options)
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
}
}
Note
This article is about serialization, not deserialization. Polymorphic deserialization is not supported, but as a workaround you can write a custom converter, such as the example in Support polymorphic deserialization.
See also
- System.Text.Json overview
- How to serialize and deserialize JSON
- Instantiate JsonSerializerOptions instances
- Enable case-insensitive matching
- Customize property names and values
- Ignore properties
- Allow invalid JSON
- Handle overflow JSON or use JsonElement or JsonNode
- Preserve references and handle circular references
- Deserialize to immutable types and non-public accessors
- Migrate from Newtonsoft.Json to System.Text.Json
- Customize character encoding
- Use DOM, Utf8JsonReader, and Utf8JsonWriter
- Write custom converters for JSON serialization
- DateTime and DateTimeOffset support
- How to use source generation
- Supported collection types
- System.Text.Json API reference
- System.Text.Json.Serialization API reference
Feedback
Submit and view feedback for