.NET 8中System.Text.Json泛型多态对象列表序列化报错求助
错误信息
System.InvalidOperationException: 'Specified type 'CSharpLanguageTestingApp.CustomResult
1[T]' is not a supported derived type for the polymorphic type 'CSharpLanguageTestingApp.CustomResult1[CSharpLanguageTestingApp.AggregateTwo]'. Derived types must be assignable to the base type, must not be generic and cannot be abstract classes or interfaces unless 'JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor' is specified.'
问题背景
在.NET 8中使用System.Text.Json序列化包含泛型基类CustomResult<T>及其派生类(dbCustomResult<T>、sbCustomResult<T>、apiCustomResult<T>)的对象列表时触发上述错误,设置FallBackToNearestAncestor也无法解决。移除列表中的CustomResult<T>对象时可正常运行,尝试过添加转换器但聚合类数量过多难以管理,自定义转换器也未成功,需要实现泛型基类与派生类在同一列表中的正常序列化/反序列化。
相关代码
示例类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Threading.Tasks; namespace CSharpLanguageTestingApp { public class AggregateOne { int iAmAggregateOne { get; set; } = 1; } public class AggregateTwo { public int iAmAggregateTwo { get; set; } = 2; } [JsonPolymorphic] [JsonDerivedType(typeof(CustomResult<>), "CustomResult")] [JsonDerivedType(typeof(dbCustomResult<>), "dbCustomResult")] [JsonDerivedType(typeof(sbCustomResult<>), "sbCustomResult")] [JsonDerivedType(typeof(apiCustomResult<>), "apiCustomResult")] public class CustomResult<T> { public int iAmAResult { get; set; } = 0; } public class dbCustomResult<T> : CustomResult<T> { public int iAmADbResult { get; set; } = 0; } public class sbCustomResult<T> : CustomResult<T> { public int iAmASbResult { get; set; } = 0; } public class apiCustomResult<T> : CustomResult<T> { public int iAmAnApiResult { get; set; } = 0; } public class CompositeResult { public string Name { get; set; } = "CompositeResult"; [JsonConverter(typeof(PolymorphicListConverter))] public List<object> IndividualResults { get; set; } = new(); } }
自定义转换器
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.Json.Serialization; using System.Text.Json; using System.Threading.Tasks; namespace CSharpLanguageTestingApp { public class PolymorphicListConverter : JsonConverter<List<object>> { public override List<object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var results = new List<object>(); // Ensure we're at the start of the array if (reader.TokenType == JsonTokenType.StartArray) { while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { if (reader.TokenType == JsonTokenType.StartObject) { using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) { JsonElement root = doc.RootElement; if (root.TryGetProperty("$type", out JsonElement typeElement)) { string typeName = typeElement.GetString(); Type type = Type.GetType(typeName); if (type != null) { var json = root.GetProperty("Data").GetRawText(); var obj = JsonSerializer.Deserialize(json, type, options); results.Add(obj); } } } } } } return results; } public override void Write(Utf8JsonWriter writer, List<object> value, JsonSerializerOptions options) { writer.WriteStartArray(); foreach (var result in value) { if (result != null) { try { // Get the runtime type of the object var resultType = result.GetType(); writer.WriteStartObject(); writer.WriteString("$type", resultType.AssemblyQualifiedName); writer.WritePropertyName("Data"); // Serialize the object as is string json = ""; try { json = JsonSerializer.Serialize(result, options); // try 1 } catch (Exception ex) { Console.WriteLine($"Error serializing object of type {result.GetType()}: {ex.Message}"); } try { JsonSerializer.Serialize(writer, result, resultType, options); // try 2 } catch (Exception ex) { Console.WriteLine($"Error serializing object of type {result.GetType()}: {ex.Message}"); } writer.WriteEndObject(); } catch (Exception ex) { Console.WriteLine($"Error serializing object of type {result.GetType()}: {ex.Message}"); throw; // Rethrow or handle the exception as needed } } } writer.WriteEndArray(); } } }
测试程序
CompositeResult compositeResult = new CompositeResult(); compositeResult.IndividualResults.Add(new dbCustomResult<AggregateOne>()); compositeResult.IndividualResults.Add(new CustomResult<AggregateTwo>()); compositeResult.IndividualResults.Add(new sbCustomResult<AggregateTwo>()); compositeResult.IndividualResults.Add(new apiCustomResult<AggregateOne>()); var options = new JsonSerializerOptions(); options.Converters.Add(new PolymorphicListConverter()); string json = ""; try { json = System.Text.Json.JsonSerializer.Serialize(compositeResult, options); } catch (Exception e) { Console.WriteLine(e.Message); } Console.WriteLine(json);
解决方案
1. 核心问题分析
System.Text.Json的[JsonPolymorphic]特性不支持直接标记泛型开放类型(如CustomResult<>)作为派生类型,这是触发错误的根本原因。默认多态校验逻辑会将开放泛型类型视为无效派生类,即使设置FallBackToNearestAncestor也无法绕过。
2. 具体修复步骤
步骤1:移除泛型基类的多态特性
删除CustomResult<T>上的[JsonPolymorphic]和[JsonDerivedType]标记,避免触发系统的多态校验:
// 移除所有多态相关特性 public class CustomResult<T> { public int iAmAResult { get; set; } = 0; }
步骤2:修改自定义转换器
调整转换器逻辑,避免递归调用,并跳过默认多态处理:
public class PolymorphicListConverter : JsonConverter<List<object>> { public override List<object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var results = new List<object>(); if (reader.TokenType == JsonTokenType.StartArray) { while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { if (reader.TokenType == JsonTokenType.StartObject) { using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) { JsonElement root = doc.RootElement; if (root.TryGetProperty("$type", out JsonElement typeElement)) { string typeName = typeElement.GetString(); Type type = Type.GetType(typeName); if (type != null) { var dataElement = root.GetProperty("Data"); // 创建新选项排除当前转换器,防止递归 var deserializeOptions = new JsonSerializerOptions(options); deserializeOptions.Converters.RemoveAll(c => c.GetType() == typeof(PolymorphicListConverter)); var obj = JsonSerializer.Deserialize(dataElement.GetRawText(), type, deserializeOptions); results.Add(obj); } } } } } } return results; } public override void Write(Utf8JsonWriter writer, List<object> value, JsonSerializerOptions options) { writer.WriteStartArray(); foreach (var result in value) { if (result != null) { var resultType = result.GetType(); writer.WriteStartObject(); writer.WriteString("$type", resultType.AssemblyQualifiedName); writer.WritePropertyName("Data"); // 创建新选项排除当前转换器,防止递归 var serializeOptions = new JsonSerializerOptions(options); serializeOptions.Converters.RemoveAll(c => c.GetType() == typeof(PolymorphicListConverter)); // 直接序列化具体封闭泛型类型 JsonSerializer.Serialize(writer, result, resultType, serializeOptions); writer.WriteEndObject(); } } writer.WriteEndArray(); } }
3. 验证
修改后重新运行测试程序,即可正常序列化和反序列化包含泛型基类及派生类的列表,无需单独为每个聚合类添加转换器。
内容的提问来源于stack exchange,提问作者Oli R

