So you’ve decided you needed to use inheritance within your object model but are struggling to serialize all the data present on the instances of the types. In a previous post, I wrote about serializing an interface instances using a TypeConverter
and you should definitely read that, as this post takes a part from that post. I’ve decided to pull the solution into its own post to make it easier to find for folks.
In this short and simple blog post, you’ll see how to take an array of varying types and serialize all the data, not just the common base class.
The Problem
Say you have a Vehicle
base class and want to derive several different vehicles like Car
and Bicycle
.
public abstract class Vehicle
{
public int NumberOfWheels { get; protected set; }
}
public class Car: Vehicle
{
public Car()
{
NumberOfWheels = 4;
}
public string Make { get; set; }
public string Model { get; set; }
}
public class Bicycle: Vehicle
{
public Bicycle()
{
NumberOfWheels = 2;
}
public string Manufacturer { get; set; }
}
And let us say you have these elements declared in a single variable of Vehicle[]
.
var vehicles = new Vehicle[]
{
new Car {Make = "Audi", Model = "A4"},
new Bicycle {Manufacturer = "Schwinn"}
};
var types = JsonSerializer.Serialize(vehicles);
When you go to use System.Text.Json
and the JsonSerializer
class, you’ll receive the following string JSON output.
[{"NumberOfWheels":4},{"NumberOfWheels":2}]
As you may have noticed, the serialization process treated each entry in our array as a Vehicle
and not the derived type. The result is because the JsonSerializer
class looks at the declared type of our parameter to determine the type when we explicitly stated the variable in our code.
The Polymorphic Serialization Solution
To get JsonSerializer
to determine the type of each instance correctly, we need to cast our Vehicle[]
to an object[]
. When the JsonSerializer
sees that a parameter type is object
, the serializer will call the GetType
method on our instances. Here’s the implementation inside of JsonSerializer
that performs the type-determination logic.
private static Type GetRuntimeType<TValue>(in TValue value)
{
if (typeof(TValue) == typeof(object) && value != null)
{
return value.GetType();
}
return typeof(TValue);
}
So, it’s surprisingly easy to get the behavior you want. Cast a collection or type to the object
type before calling JsonSerializer.Serialize
to get polymorphic serialization.
static void Main(string[] args)
{
var vehicles = new Vehicle[]
{
new Car {Make = "Audi", Model = "A4"},
new Bicycle {Manufacturer = "Schwinn"}
};
// All types will be serialized a the base class
// of Vehicle, likely not what you want to happen
var types = JsonSerializer.Serialize(vehicles);
Console.WriteLine($"As Vehicle[]: {types}");
// ** The Solution **
// cast your collection to object[]
// or cast a single instance to an object
var objects = JsonSerializer.Serialize((object[]) vehicles);
Console.WriteLine($"As Object[]: {objects}");
}
Running the code above, you’ll see the following results in your console output.
As Vehicle[]: [{"NumberOfWheels":4},{"NumberOfWheels":2}]
As Object[]: [{"Make":"Audi","Model":"A4","NumberOfWheels":4},{"Manufacturer":"Schwinn","NumberOfWheels":2}]
Great! I hope you found this post helpful, and as always, thanks for reading.