title: Polymorphic deserialization to list of derived class - System.Text.Json.JsonConverter
body: <p>Based on the examples posted <a href="https://stackoverflow.com/questions/58074304/is-polymorphic-deserialization-possible-in-system-text-json">here on StackOverflow</a> and in the <a href="https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-converters-how-to?pivots=dotnet-5-0#support-polymorphic-deserialization" rel="nofollow noreferrer">Microsoft documentation</a>, I tried to get polymorphic deserialization to work.</p>
  <p>The example in the accepted answer given on the StackOverflow post works perfectly, as long as the object list you're trying to (De)Serialize is of the type <code>List&lt;BaseClass&gt;</code>.</p>
  <p>I now need to (De)Serialize a list of <code>DerivedA</code> classes, which throws the error</p>
  <blockquote>
  <p>InvalidCastException: Unable to cast object of type
  'BaseClassConverter' to type
  'System.Text.Json.Serialization.JsonConverter`1[DerivedA]'.</p>
  </blockquote>
  <p>I think I understand this is because of the <a href="https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance" rel="nofollow noreferrer">lack of covariance</a> in the JsonConverter generic implementation.</p>
  <p>Unfortunately, I do not understand nor did I find a possible work around or implementation to get my situation to work.</p>
  <p>In case something happens with the answer I've linked to, this is the code I'm trying to get working:</p>
  <pre><code>var objects = new List&lt;DerivedA&gt; { new DerivedA(), new DerivedA() };
  
  // Using: System.Text.Json
  var options = new JsonSerializerOptions
  {
      Converters = { new BaseClassConverter() },
      WriteIndented = true
  };
  
  string jsonString = JsonSerializer.Serialize(objects, options);
  Console.WriteLine(jsonString);
  
  public class BaseClass
  {
      public int Int { get; set; }
  }
  public class DerivedA : BaseClass
  {
      public string Str { get; set; }
  }
  public class DerivedB : BaseClass
  {
      public bool Bool { get; set; }
  }
  
  
  public class BaseClassConverter : JsonConverter&lt;BaseClass&gt;
  {
      private enum TypeDiscriminator
      {
          BaseClass = 0,
          DerivedA = 1,
          DerivedB = 2
      }
  
      public override bool CanConvert(Type type)
      {
          return typeof(BaseClass).IsAssignableFrom(type);
      }
  
      public override BaseClass Read(
          ref Utf8JsonReader reader,
          Type typeToConvert,
          JsonSerializerOptions options)
      {
          if (reader.TokenType != JsonTokenType.StartObject)
          {
              throw new JsonException();
          }
  
          if (!reader.Read()
                  || reader.TokenType != JsonTokenType.PropertyName
                  || reader.GetString() != &quot;TypeDiscriminator&quot;)
          {
              throw new JsonException();
          }
  
          if (!reader.Read() || reader.TokenType != JsonTokenType.Number)
          {
              throw new JsonException();
          }
  
          BaseClass baseClass;
          TypeDiscriminator typeDiscriminator = (TypeDiscriminator)reader.GetInt32();
          switch (typeDiscriminator)
          {
              case TypeDiscriminator.DerivedA:
                  if (!reader.Read() || reader.GetString() != &quot;TypeValue&quot;)
                  {
                      throw new JsonException();
                  }
                  if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
                  {
                      throw new JsonException();
                  }
                  baseClass = (DerivedA)JsonSerializer.Deserialize(ref reader, typeof(DerivedA));
                  break;
              case TypeDiscriminator.DerivedB:
                  if (!reader.Read() || reader.GetString() != &quot;TypeValue&quot;)
                  {
                      throw new JsonException();
                  }
                  if (!reader.Read() || reader.TokenType != JsonTokenType.StartObject)
                  {
                      throw new JsonException();
                  }
                  baseClass = (DerivedB)JsonSerializer.Deserialize(ref reader, typeof(DerivedB));
                  break;
              default:
                  throw new NotSupportedException();
          }
  
          if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject)
          {
              throw new JsonException();
          }
  
          return baseClass;
      }
  
      public override void Write(
          Utf8JsonWriter writer,
          BaseClass value,
          JsonSerializerOptions options)
      {
          writer.WriteStartObject();
  
          if (value is DerivedA derivedA)
          {
              writer.WriteNumber(&quot;TypeDiscriminator&quot;, (int)TypeDiscriminator.DerivedA);
              writer.WritePropertyName(&quot;TypeValue&quot;);
              JsonSerializer.Serialize(writer, derivedA);
          }
          else if (value is DerivedB derivedB)
          {
              writer.WriteNumber(&quot;TypeDiscriminator&quot;, (int)TypeDiscriminator.DerivedB);
              writer.WritePropertyName(&quot;TypeValue&quot;);
              JsonSerializer.Serialize(writer, derivedB);
          }
          else
          {
              throw new NotSupportedException();
          }
  
          writer.WriteEndObject();
      }
  }
  </code></pre>