Hexalith.PolymorphicSerializations provides robust support for polymorphic serialization and deserialization in .NET applications, integrating seamlessly with System.Text.Json. It simplifies handling complex object hierarchies where instances of derived types need to be serialized and deserialized based on a common base type or interface.
This library is particularly useful when dealing with scenarios like:
- Event Sourcing (serializing different domain events)
- Message Queues (sending/receiving various message types)
- APIs returning different response types based on context
- Configuration systems loading diverse component types
- NoSQL Databases
- Type Discrimination - Automatically includes type information in serialized JSON to ensure proper deserialization.
- Custom Type Resolution - Flexible system for mapping between .NET types and JSON discriminator values.
- Minimal Configuration - Simple attribute-based setup with reasonable defaults.
- High Performance - Built on the high-performance
System.Text.Jsonserialization engine. - Framework Agnostic - Works with any .NET application using .NET Standard 2.0 or higher.
- No External Dependencies - Only depends on
System.Text.Json.
- .NET 10.0 or higher
Install the packages via NuGet Package Manager or the .NET CLI:
dotnet add package Hexalith.PolymorphicSerializations
dotnet add package Hexalith.PolymorphicSerializations.CodeGeneratorsThe library uses source generators to create serialization mappers at compile time. This requires:
- Adding
partialkeyword to your classes/records - Applying
[PolymorphicSerialization]attributes - Installing the code generator package
// Basic class with no inheritance
// The 'partial' keyword allows the source generator to extend this type
// The serialized JSON will include a discriminator with value "Car"
[PolymorphicSerialization]
public partial record Car(string Name, string EnergyType);
// Polymorphic inheritance example
// Base class must be marked as polymorphic
[PolymorphicSerialization]
public abstract partial record Animal(string Name);
// Derived class specifying its base type
// The serialized JSON will include a discriminator with value "Dog"
[PolymorphicSerialization(baseType: typeof(Animal))]
public partial record Dog(string Name, bool Dangerous) : Animal(Name);
// Another derived class
// The serialized JSON will include a discriminator with value "Cat"
[PolymorphicSerialization(baseType: typeof(Animal))]
public partial record Cat(string Name, string Color) : Animal(Name);
// Versioned class example
// The serialized JSON will include a discriminator with value "CatV2"
// This supports versioning of your data models
[PolymorphicSerialization("Cat", 2, typeof(Animal))]
public partial record NewCatVersion2(string Name, bool LikesCatnip) : Animal(Name);The source generator creates a registration extension method in your project's namespace. The generated class follows the pattern {AssemblyName}Serialization:
using System.Text.Json;
using MyProject.Extensions; // Contains the generated extension method
using Hexalith.PolymorphicSerializations;
// Register all polymorphic mappers defined in your project
// This connects your model classes to the serialization system
// The generated class name follows the pattern: {AssemblyName}Serialization
MyProjectSerialization.RegisterPolymorphicMappers();
// Serialize the Car object. You need to specify polymorphic deserialization by using the Polymorphic type.
string json = JsonSerializer.Serialize<Polymorphic>(new Car("Volvo", "Electric"), PolymorphicHelper.DefaultJsonSerializerOptions);
// Deserialize the Car object. You need to specify polymorphic deserialization by using the Polymorphic type.
var value = JsonSerializer.Deserialize<Polymorphic>(json, PolymorphicHelper.DefaultJsonSerializerOptions);For more detailed usage examples and demonstrations of various features, explore the sample applications:
- Examples README
- Includes examples like deserializing polymorphic messages from files (
DeserializeFileMessages).
- Includes examples like deserializing polymorphic messages from files (
The repository is organized as follows:
- src Is the source code directory where you will add your package projects.
- test Contains test projects for your packages.
- examples Contains example implementations of your packages.
- Hexalith.Builds Contains shared build configurations and tools.
Contributions are welcome! Here's how you can contribute:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Please ensure your code follows the project's coding standards and includes appropriate tests.
- Type not recognized during deserialization: Ensure you've applied the correct attributes and registered the converter with your JsonSerializerOptions.
- Missing type discriminator: Check that your base class has the [PolymorphicSerialization] attribute.
- Serialization exceptions: Verify that all derived types have the [PolymorphicSerialization] attribute with the correct baseType parameter.
- Build errors: Make sure all classes with polymorphic attributes are marked as
partial.
This project is licensed under the MIT License - see the LICENSE file for details.
When referencing the Hexalith.PolymorphicSerializations.CodeGenerators package in your projects, ensure you set it up correctly to work as an analyzer:
<ItemGroup>
<!-- Reference the main package -->
<PackageReference Include="Hexalith.PolymorphicSerializations" />
<!-- Reference the CodeGenerators package as an analyzer -->
<PackageReference Include="Hexalith.PolymorphicSerializations.CodeGenerators" PrivateAssets="all" />
</ItemGroup>The PrivateAssets="all" attribute is critical for the source generator to work properly, ensuring that the analyzer is only used during compilation and isn't referenced by consumers of your assembly.
If you continue to have issues with the analyzer not being detected, you can try adding this more explicit reference:
<ItemGroup>
<PackageReference Include="Hexalith.PolymorphicSerializations" />
<PackageReference Include="Hexalith.PolymorphicSerializations.CodeGenerators" PrivateAssets="all" GeneratePathProperty="true" />
<Analyzer Include="$(PkgHexalith_PolymorphicSerializations_CodeGenerators)\analyzers\dotnet\cs\Hexalith.PolymorphicSerializations.CodeGenerators.dll" />
</ItemGroup>Make sure to rebuild your solution after making these changes.