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

C#如何在SkillConverter中使用SkillEffectConverter反序列化嵌套多态对象

解决嵌套基类反序列化时的转换器调用问题

一、Newtonsoft.Json的可行方案

你之前的崩溃大概率是因为在SkillConverter中手动处理Effects属性时,没有调用已实现的SkillEffectConverter,导致多态类型无法正确识别。核心解决思路是:在SkillConverter的ReadJson方法中,利用当前JsonSerializer实例的Populate方法完成属性填充,让序列化器自动调用已配置的SkillEffectConverter处理嵌套的SkillEffect类型。

完整可运行代码

Part 1:正常工作的SkillEffect基类及转换器

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;

// SkillEffect基类与子类
public abstract class SkillEffect
{
    public string EffectType { get; set; }
}

public class DamageEffect : SkillEffect
{
    public int Damage { get; set; }
}

public class SlowEffect : SkillEffect
{
    public float SlowPercent { get; set; }
}

// SkillEffect自定义转换器
public class SkillEffectConverter : CustomCreationConverter<SkillEffect>
{
    public override SkillEffect Create(Type objectType)
    {
        throw new NotImplementedException("Create无需直接调用,使用ReadJson处理实例创建");
    }

    public override bool CanWrite => false; // 仅处理反序列化

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader);
        var effectType = jObject["EffectType"].Value<string>();

        var effect = effectType switch
        {
            "Damage" => new DamageEffect(),
            "Slow" => new SlowEffect(),
            _ => throw new JsonSerializationException($"未知效果类型: {effectType}")
        };

        // 用序列化器填充属性,确保所有字段被正确反序列化
        serializer.Populate(jObject.CreateReader(), effect);
        return effect;
    }
}

Part 2:修复后的Skill基类及转换器(原崩溃代码的修复版)

// Skill基类与子类
public abstract class Skill
{
    public string SkillType { get; set; }
    public List<SkillEffect> Effects { get; set; } = new List<SkillEffect>();
}

public class ProjectileSkill : Skill
{
    public float Speed { get; set; }
    public float Range { get; set; }
}

// Skill自定义转换器
public class SkillConverter : CustomCreationConverter<Skill>
{
    public override Skill Create(Type objectType)
    {
        throw new NotImplementedException("Create无需直接调用,使用ReadJson处理实例创建");
    }

    public override bool CanWrite => false;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jObject = JObject.Load(reader);
        var skillType = jObject["SkillType"].Value<string>();

        var skill = skillType switch
        {
            "Projectile" => new ProjectileSkill(),
            _ => throw new JsonSerializationException($"未知技能类型: {skillType}")
        };

        // 关键:使用serializer.Populate自动调用已配置的SkillEffectConverter处理Effects
        serializer.Populate(jObject.CreateReader(), skill);
        return skill;
    }
}

测试代码

class Program
{
    static void Main(string[] args)
    {
        // 配置序列化器,添加两个转换器
        var settings = new JsonSerializerSettings();
        settings.Converters.Add(new SkillEffectConverter());
        settings.Converters.Add(new SkillConverter());

        // 测试JSON
        var json = @"
        {
            ""SkillType"": ""Projectile"",
            ""Speed"": 10.0,
            ""Range"": 50.0,
            ""Effects"": [
                {
                    ""EffectType"": ""Damage"",
                    ""Damage"": 20
                },
                {
                    ""EffectType"": ""Slow"",
                    ""SlowPercent"": 0.5
                }
            ]
        }";

        // 反序列化(修复后无崩溃)
        var skill = JsonConvert.DeserializeObject<Skill>(json, settings);
        if (skill is ProjectileSkill projectile)
        {
            Console.WriteLine($"投射技能:速度={projectile.Speed},射程={projectile.Range}");
            foreach (var effect in projectile.Effects)
            {
                switch (effect)
                {
                    case DamageEffect de:
                        Console.WriteLine($"伤害效果:{de.Damage}点");
                        break;
                    case SlowEffect se:
                        Console.WriteLine($"减速效果:{se.SlowPercent:P0}");
                        break;
                }
            }
        }
    }
}

崩溃原因说明

如果之前的SkillConverter中手动用jObject["Effects"].ToObject<List<SkillEffect>>()处理嵌套属性,而没有传入配置了SkillEffectConverter的JsonSerializerSettings,就会因为Newtonsoft无法识别SkillEffect的子类而抛出类型转换异常。使用serializer.Populate则会自动复用当前序列化器已配置的所有转换器,正确处理嵌套的多态类型。

二、替代方案:System.Text.Json

如果Newtonsoft.Json的方案不符合需求,可以使用.NET内置的System.Text.Json,它原生支持多态反序列化,无需手动编写转换器,实现更简洁:

using System.Text.Json;
using System.Text.Json.Serialization;

// 配置SkillEffect多态鉴别器
[JsonDerivedType(typeof(DamageEffect), typeDiscriminator: "Damage")]
[JsonDerivedType(typeof(SlowEffect), typeDiscriminator: "Slow")]
public abstract class SkillEffect
{
    public string EffectType { get; set; }
}

public class DamageEffect : SkillEffect
{
    public int Damage { get; set; }
}

public class SlowEffect : SkillEffect
{
    public float SlowPercent { get; set; }
}

// 配置Skill多态鉴别器
[JsonDerivedType(typeof(ProjectileSkill), typeDiscriminator: "Projectile")]
public abstract class Skill
{
    public string SkillType { get; set; }
    public List<SkillEffect> Effects { get; set; } = new List<SkillEffect>();
}

public class ProjectileSkill : Skill
{
    public float Speed { get; set; }
    public float Range { get; set; }
}

// 测试代码
class Program
{
    static void Main(string[] args)
    {
        var json = @"
        {
            ""SkillType"": ""Projectile"",
            ""Speed"": 10.0,
            ""Range"": 50.0,
            ""Effects"": [
                {
                    ""EffectType"": ""Damage"",
                    ""Damage"": 20
                },
                {
                    ""EffectType"": ""Slow"",
                    ""SlowPercent"": 0.5
                }
            ]
        }";

        var skill = JsonSerializer.Deserialize<Skill>(json, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        });

        // 后续使用逻辑与之前一致
    }
}

内容的提问来源于stack exchange,提问作者Jan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 11:30:48