基于文本属性使用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 } ] }
解决方案
错误根源
- 加载
JObject后,JsonReader指针已移至对象末尾,后续调用serializer.Deserialize无法读取有效数据,导致生成空对象。 _types数组重复添加typeof(Widget),属于冗余代码。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); } }
关键修改说明
- 清理
_types数组的冗余项,保持类型列表简洁。 - 修正
JsonToken.Null分支的返回值,确保与目标类型兼容。 - 改用
JObject.ToObject<T>方法反序列化,避免因JsonReader指针偏移导致的空对象问题。 - 统一
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
相关产品推荐
相关产品推荐

