Configure polymorphic JSON serialization using [JsonPolymorphic] and [JsonDerivedType] attributes for type-safe inheritance hierarchies.
When to Use
- •Serializing inheritance hierarchies with System.Text.Json
- •Adding type discriminators for polymorphic deserialization
- •Preparing polymorphic types for AOT-compatible JSON source generation
- •Replacing Newtonsoft.Json
TypeNameHandlingwith modern attributes
Requirements
- •.NET 7 or higher
Steps
- •
Ask scope:
- •Ask user: "Apply to entire solution or specific project?"
- •If specific project, ask which project
- •
Search for existing
[JsonPolymorphic]attributes:- •Detect existing project conventions
- •Note any
TypeDiscriminatorPropertyNamevalues found
- •
Extract discriminator conventions:
- •Search for
TypeDiscriminatorPropertyNamein existing attributes - •Record all unique discriminator property names found
- •Search for
- •
Find polymorphic candidates:
- •Search for abstract classes with derived types
- •Search for interfaces with implementing classes used in serialization
- •Look for classes with virtual members that are inherited
- •
Ask about discriminator property name:
- •If existing discriminators found, show them to user
- •Ask: "What discriminator property name? (Default:
$type, Found: [list existing])"
- •
Check for consistency:
- •If multiple different discriminators found in codebase
- •Ask: "Found multiple discriminators: [list]. Standardize to single value?"
- •
Apply attributes to base types:
csharp[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")] [JsonDerivedType(typeof(DerivedA), typeDiscriminator: "DerivedA")] [JsonDerivedType(typeof(DerivedB), typeDiscriminator: "DerivedB")] public abstract class BaseClass { }- •Use full type name as discriminator value (e.g.,
"WeatherForecastWithCity")
- •Use full type name as discriminator value (e.g.,
- •
Add using directive:
- •Ensure
using System.Text.Json.Serialization;is present
- •Ensure
- •
Verify with build:
bashdotnet build
- •
Report results:
- •List all base types configured
- •List all derived type mappings added
- •Confirm build status
Example Conversion
Before:
csharp
public abstract class Notification
{
public string Message { get; set; }
}
public class EmailNotification : Notification
{
public string EmailAddress { get; set; }
}
public class SmsNotification : Notification
{
public string PhoneNumber { get; set; }
}
After:
csharp
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(EmailNotification), typeDiscriminator: "EmailNotification")]
[JsonDerivedType(typeof(SmsNotification), typeDiscriminator: "SmsNotification")]
public abstract class Notification
{
public string Message { get; set; }
}
public class EmailNotification : Notification
{
public string EmailAddress { get; set; }
}
public class SmsNotification : Notification
{
public string PhoneNumber { get; set; }
}
JSON Output:
json
{
"$type": "EmailNotification",
"message": "Hello",
"emailAddress": "user@example.com"
}
Notes
- •AOT Compatibility: Metadata-based source generation is supported; fast-path source generation is NOT supported for polymorphic types
- •Serialization requirement: Must serialize using the base type for polymorphism to work (e.g.,
JsonSerializer.Serialize<Notification>(emailNotification)) - •Discriminator values: String discriminators are recommended over integers for readability and forward compatibility
- •Nested hierarchies: Each level in the hierarchy needs its own
[JsonPolymorphic]attribute if it has derived types - •Interface support: Interfaces can also use
[JsonPolymorphic]when they define the contract for serialization