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

为何JsonConvert.DeserializeObject非泛型方法不使用指定JsonConverter?

问题分析与解决方案

你遇到的这个问题是Newtonsoft.Json在处理带$type的非泛型反序列化时的一个典型行为差异:泛型DeserializeObject<T>会优先检查全局转换器是否匹配目标类型T,而非泛版则会先通过$type解析出类型,再默认使用该类型的构造函数创建实例,不会主动触发全局转换器(除非类型契约明确指定)。

从你的示例输出也能看出来:泛型调用时走了ExampleConverter.ReadJson,而非泛型调用直接触发了Example的默认构造函数,完全跳过了转换器。

解决方案

下面提供三种可行的解决思路,你可以根据自己的场景选择:


1. 给目标类型标记[JsonConverter]特性(最简单直接)

如果可以修改目标类的代码,直接在类上标记对应的转换器特性,这样不管是泛型还是非泛型反序列化,都会强制使用你的转换器:

[JsonConverter(typeof(ExampleConverter))]
public class Example {
    public Example(Guid guid) {
        Console.WriteLine("...Example Ctor: " + guid.ToString());
    }
    public string ExampleProp { get; set; }
}

修改后再运行你的测试,非泛型反序列化就会触发ExampleConverter.ReadJson,不再调用默认构造函数。


2. 自定义契约解析器(不修改目标类的情况)

如果不能修改目标类的代码,可以通过自定义IContractResolver,为指定类型绑定转换器,让Json.NET在解析$type后仍然使用你的转换器:

public class CustomContractResolver : DefaultContractResolver {
    private readonly JsonConverter _targetConverter;
    private readonly Type _targetType;

    public CustomContractResolver(Type targetType, JsonConverter converter) {
        _targetType = targetType;
        _targetConverter = converter;
    }

    protected override JsonContract CreateContract(Type objectType) {
        var contract = base.CreateContract(objectType);
        // 为目标类型绑定转换器
        if (objectType == _targetType) {
            contract.Converter = _targetConverter;
        }
        return contract;
    }
}

然后在反序列化配置中使用这个契约解析器:

var readSettings = new JsonSerializerSettings() {
    TypeNameHandling = TypeNameHandling.All,
    ContractResolver = new CustomContractResolver(typeof(Example), new ExampleConverter())
};

这样非泛型反序列化时,Json.NET解析出Example类型后,会自动使用你指定的转换器。


3. 手动解析$type后调用泛型方法(灵活适配多类型场景)

如果需要处理多种类型,不想逐个绑定转换器,可以先手动读取JSON中的$type字段,获取目标类型后通过反射调用泛版DeserializeObject:

// 先解析$type获取目标类型
var jObject = JObject.Parse(json);
var typeFullName = (string)jObject["$type"];
var targetType = Type.GetType(typeFullName);

// 反射调用泛型反序列化方法
var deserializeMethod = typeof(JsonConvert)
    .GetMethod("DeserializeObject", new[] { typeof(string), typeof(JsonSerializerSettings) })
    .MakeGenericMethod(targetType);
var e3 = deserializeMethod.Invoke(null, new object[] { json, readSettings });

这种方式本质上还是复用了泛型反序列化的逻辑,自然会触发你的全局转换器。


针对IOC容器的适配

你提到需要通过IOC容器解析实例,只需要在转换器的ReadJson方法中替换实例创建逻辑即可,比如:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer){
    Console.WriteLine("...ExampleConverter.ReadJson...");
    // 从IOC容器获取实例(这里替换成你的容器调用逻辑)
    var instance = YourIocContainer.Resolve(objectType);
    // 填充属性
    serializer.Populate(reader, instance);
    return instance;
}

这样就完全不需要目标类有默认构造函数,所有实例都通过IOC创建并完成数据填充。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:08:32