3

So for instance I have several types of cars that are being serialized to a .car file (this is a school project). I have three different types, ford, lexus, and dodge. I can save them fine. But with the current architecture of my program, when I deserialize I need to know the type before deserializing. For instance I am serializing like this:

if (CurrentCar.GetType() == typeof(Ford))
{
   var JSON_CAR = JsonSerializer.Serialize((Ford)CurrentCar);
   writer.Write(JSON_CAR);
}

When I deserialize, I need to know the type, before deserializing:

CurrentCar = JsonSerializer.Deserialize<???>(reader.ReadString());

How can I achieve this? Thank you.

Connor
  • 113
  • 8
  • 1
    You have to post your json if you need some help. And your classes too. – Serge Nov 27 '21 at 01:18
  • Connor Have you found a better way to do this – cp5 Mar 03 '22 at 12:55
  • @Chrispie unfortunately no, I had to follow this tutorial: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-6-0#support-polymorphic-deserialization. It's sort of overbearing, but there's no better way I've found to it. – Connor Mar 06 '22 at 00:19

1 Answers1

6

update .NET 7

Now the latest and greatest .NET 7 provides Full support for polymorphic serialization and deserialization.

you just need to add a little of metadata for the base class and you are good to go

[JsonPolymorphic(TypeDiscriminatorPropertyName = "$CarManufacturer")]
[JsonDerivedType(typeof(Ford), "Ford")]
[JsonDerivedType(typeof(BMW),"BMW")]
class Car
{
    public string Name { get; set; }
}

class Ford : Car
{
    public string OnlyFord { get; set; }
}

class BMW : Car
{
    public string OnlyBMW { get; set; }
}

Serialize

Car CurrentCar = new Ford { Name = "Foard Car", OnlyFord = "spacific feature in Ford Cars" };
string json = JsonSerializer.Serialize(CurrentCar);
Console.WriteLine(json);

this will generate the following json

{"$CarManufacturer":"Ford","OnlyFord":"spacific feature in Ford Cars","Name":"Foard Car"}

Deserialize

var car = JsonSerializer.Deserialize<Car>(json);
Console.WriteLine(car is Ford); // true

old answer (.NET 6 or an earlier version)

If you don't know the exact type of the car, your Json should contain property hold its type

{
  "CarManufacturer" : "FORD"
}

you can add this property manually to your class

enum CarManufacturer
{
    FORD,
    BMW
}
class Car
{
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public CarManufacturer Manufacturer { get; set; }
    public string Name { get; set; }
}

class Ford:Car
{
    public Ford()
    {
        Manufacturer = CarManufacturer.FORD;
    }
    public string OnlyFord { get; set; }
}

class BMW :Car
{
    public BMW()
    {
        Manufacturer = CarManufacturer.BMW; ;
    }
    public string OnlyBMW { get; set; }
}

and you can serialize it as following

Car CurrentCar = new Ford { Name = "Foard Car", OnlyFord = "spacific feature in Ford Cars" };
string json = JsonSerializer.Serialize(CurrentCar, CurrentCar.GetType());
Console.WriteLine(json);

to Deserialize

Car Tempcar = JsonSerializer.Deserialize<Car>(json);
var Car = Tempcar.Manufacturer switch
{
    CarManufacturer.BMW => JsonSerializer.Deserialize<BMW>(json),
    CarManufacturer.FORD => JsonSerializer.Deserialize<Ford>(json),
    _ => Tempcar
};

This should solve your problem, but it has some disadvantages

  1. You will make deserialization two times for same object !
  2. Adding some property to your models to work as Type Discriminator
  3. This solution will not work if your base class is an abstract class

to overcome this problem, you should write Custom converter Support polymorphic deserialization this will automatically add Type Discriminator to the generated json without adding any value to your Class

Ibram Reda
  • 1,882
  • 2
  • 7
  • 18
  • Thanks for the educational answer! Would another way be to have a container class with a bunch of nullable properties, one for every possible type of Car, and serialise that container class? To me that sounds like it'd be the equivalent of Google Protocol Buffers OneOf, or ASN.1's CHOICE, which is what it sounds like the questioner wanted. I don't know if C# can handle such a class when serialising to JSON (am no expert in C#), but if it did work then it'd be a single de-serialisation. Possibly another way would be to actually use GPB or ASN.1 with JSON wireformat, save writing a lot of code. – bazza Nov 28 '21 at 22:34