With .NET 8, working with polymorphic JSON serialization and deserialization is easier and more effective. Earlier versions of System.Text.Json had issues handling polymorphism, but .NET 8 now offers built-in support that eliminates the need for workarounds or additional libraries.
Polymorphic Serialization
Polymorphic serialization involves converting an object to JSON using its base class or interface, while also retaining information (such as its actual type) to ensure the correct derived class is restored during deserialization.
Polymorphic Deserialization
Polymorphic deserialization involves converting JSON back into the correct derived class, rather than just the base class.
.NET 8 builds on the capabilities introduced in .NET 7 by further enhancing support for polymorphic serialization through the JsonDerivedType attribute, JsonPolymorphismOptions, and JsonPolymorphic.
The source code can be downloaded from this GitRepo.
In our example, the MessageRequest JSON includes a nested Message object, which can represent either an Email or an Sms, each with its own specific fields. The MessageType property is used to identify the type of message.
This explanation covers a simple approach known as declarative polymorphism, which leverages attributes.
namespace PolymorphicDeserialization.Models
{
[JsonPolymorphic(TypeDiscriminatorPropertyName = "MessageType")]
[JsonDerivedType(typeof(Email), "Email")]
[JsonDerivedType(typeof(Sms), "Sms")]
public abstract class Message
{
public string MessageBody { get; set; }
}
public class Email : Message
{
public string EmailAddress { get; set; }
}
public class Sms : Message
{
public string PhoneNumber { get; set; }
}
public class MessageRequest
{
public string RequestId { get; set; }
public Message Message { get; set; }
}
}
To properly handle serialization and deserialization.
- We define a base class, Message, and
- Apply the [JsonPolymorphic] attribute with MessageType as the discriminator.
- Apply the [JsonDerivedType] attribute to the base class Message, specifying the derived classes Email and Sms, along with their corresponding MessageType values (Email or Sms).
Note. You don’t need to explicitly define the MessageType field in the Email and Sms classes. It is automatically included during serialization and leveraged during deserialization to determine the appropriate class.
Common Mistakes to Avoid
- Missing Type Field: If the "MessageType" field is missing, the system will not know which specific class to convert the data into.
- Case Sensitivity: The "MessageType" value must match exactly, including uppercase and lowercase letters, unless you handle it differently.
- Interface vs. Abstract Class: This works with both interfaces and abstract classes, but make sure you use one style consistently.
I just ran the code and captured screenshots for both deserialization and serialization.
Deserialization
![Deserialization]()
Serialization
![Serialization]()
Conclusion
Polymorphic deserialization in .NET 8 is now a built-in, first-class feature. It makes handling different object types easier in scenarios like message processing, APIs, or document-based storage.
Happy Coding!