You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8中System.Text.Json泛型多态对象列表序列化报错求助

.NET 8中System.Text.Json序列化泛型基类派生类列表的问题解决

错误信息

System.InvalidOperationException: 'Specified type 'CSharpLanguageTestingApp.CustomResult1[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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:20:55