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

基于文本属性使用Newtonsoft反序列化对象时的异常问题

问题描述

使用JsonConvert.DeserializeObject(json)反序列化时返回一组空对象;若让对象不继承Widget类,会出现以下错误:

The value "Widget2" is not of type "Widget" and cannot be used in this generic collection. (Parameter 'value')

需求:反序列化一组对象,通过对象中的widgetType属性指定反序列化的目标类型。


相关代码与JSON字符串

C# 代码

public class widgets
{
    public IEnumerable<Widget> Widgets { get; set; }
}

[JsonConverter(typeof(WidgetConverter))]
public class Widget
{
    public string widgetType { get; set; }
    public string name { get; set; }
}

public class Widget2 : Widget
{
    public string text { get; set; }
}

public class Widget3 : Widget
{
    public int sourceId { get; set; }
}

public class WidgetConverter : JsonConverter
{
    private readonly Type[] _types = (new List<Type> { typeof(Widget), typeof(Widget), typeof(Widget2), typeof(Widget3) }).ToArray();

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return string.Empty;
        }
        else if (reader.TokenType == JsonToken.EndObject)
        {
            return null;
        }
        else if (reader.TokenType == JsonToken.String)
        {
            return serializer.Deserialize(reader, objectType);
        }
        else
        {
            JObject obj = JObject.Load(reader);
            var widgetType = obj.GetValue("WidgetType", StringComparison.InvariantCultureIgnoreCase)?.Value<string>().ToLower();
            switch (widgetType)
            {
                case "widget2":
                    var textWidget = serializer.Deserialize(reader, typeof(Widget2));
                    return textWidget;
                case "widget3":
                    var datavizWidget = serializer.Deserialize(reader, typeof(Widget3));
                    return datavizWidget;
                default:
                    var widget = serializer.Deserialize(reader, typeof(Widget));
                    return widget;
            }
        }
    }

    public override bool CanConvert(Type typeToConvert)
    {
        return _types.Any(t => t == typeToConvert);
    }
}

JSON 字符串

{ "widgets": 
    [
        {
            "widgetType": "widget2",
            "name": "Widget type 2",
            "text": "Something is happening"
        },
        {
            "widgetType": "widget3",
            "name": "Widget type 3",
            "sourceId": 1
        }
    ]
}

解决方案

错误根源

  1. 加载JObject后,JsonReader指针已移至对象末尾,后续调用serializer.Deserialize无法读取有效数据,导致生成空对象。
  2. _types数组重复添加typeof(Widget),属于冗余代码。
  3. JsonToken.Null分支返回string.Empty,与目标类型Widget不兼容,可能引发类型错误。

修正后的代码

public class widgets
{
    public IEnumerable<Widget> Widgets { get; set; }
}

[JsonConverter(typeof(WidgetConverter))]
public class Widget
{
    public string widgetType { get; set; }
    public string name { get; set; }
}

public class Widget2 : Widget
{
    public string text { get; set; }
}

public class Widget3 : Widget
{
    public int sourceId { get; set; }
}

public class WidgetConverter : JsonConverter
{
    // 移除重复的typeof(Widget),精简类型列表
    private readonly Type[] _types = { typeof(Widget), typeof(Widget2), typeof(Widget3) };

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            // 返回null而非string.Empty,匹配Widget类型
            return null;
        }
        else if (reader.TokenType == JsonToken.EndObject)
        {
            return null;
        }
        else if (reader.TokenType == JsonToken.String)
        {
            return serializer.Deserialize(reader, objectType);
        }
        else
        {
            JObject obj = JObject.Load(reader);
            var widgetType = obj.GetValue("widgetType", StringComparison.InvariantCultureIgnoreCase)?.Value<string>().ToLower();
            
            // 使用JObject.ToObject<T>反序列化,避免Reader指针位置问题
            switch (widgetType)
            {
                case "widget2":
                    return obj.ToObject<Widget2>(serializer);
                case "widget3":
                    return obj.ToObject<Widget3>(serializer);
                default:
                    return obj.ToObject<Widget>(serializer);
            }
        }
    }

    public override bool CanConvert(Type typeToConvert)
    {
        return _types.Any(t => t == typeToConvert);
    }
}

关键修改说明

  1. 清理_types数组的冗余项,保持类型列表简洁。
  2. 修正JsonToken.Null分支的返回值,确保与目标类型兼容。
  3. 改用JObject.ToObject<T>方法反序列化,避免因JsonReader指针偏移导致的空对象问题。
  4. 统一widgetType的大小写匹配逻辑,与JSON字段保持一致。

测试代码

string json = @"
{ ""widgets"": 
    [
        {
            ""widgetType"": ""widget2"",
            ""name"": ""Widget type 2"",
            ""text"": ""Something is happening""
        },
        {
            ""widgetType"": ""widget3"",
            ""name"": ""Widget type 3"",
            ""sourceId"": 1
        }
    ]
}";

var result = JsonConvert.DeserializeObject<widgets>(json);
// 此时result.Widgets会包含正确的Widget2和Widget3实例,所有属性值正常填充

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 06:00:02