.NET Framework 4.7.2 Newtonsoft.Json反序列化嵌套JSON异常
解决Newtonsoft.Json反序列化混合类型Value字段的异常问题
问题背景
在.NET Framework 4.7.2环境下,调用Web API获取数据并使用Newtonsoft.Json反序列化时,API返回的时间/值对数组中,Value字段存在两种格式:
- 正常情况:
Value为普通数值,反序列化正常 - 异常/无数据情况:
Value为嵌套JSON对象,此时抛出异常:Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Object' because the type requires a JSON primitive value
问题原因
Newtonsoft.Json在反序列化object类型时,默认仅支持JSON原始值(字符串、数字、布尔、null)的转换。当遇到JSON对象时,由于无法自动推断对应的.NET类型,会直接抛出上述异常。
解决方案
以下提供三种可行的解决方式,根据需求选择:
方案1:用JToken替代object类型(推荐)
将PiValue类的Value属性类型改为JToken,它可以兼容任何JSON元素(数值、对象、数组等),再在属性逻辑中统一处理为业务需要的double值:
[JsonObject] public class PiValue { private DateTime _Timestamp = DateTime.MinValue; private double _Value = 0d; /// <summary> /// Always gets set to Local Time /// </summary> [JsonProperty] public DateTime Timestamp { get { return _Timestamp; } set { _Timestamp = value.ToLocalTime(); } } [JsonProperty] public JToken Value { get { return JToken.FromObject(_Value); } set { if (value.Type == JTokenType.Object) { // 嵌套对象直接设为NaN _Value = double.NaN; } else { // 尝试转换为double,失败则设为NaN _Value = value.ToObject<double?>() ?? double.NaN; } } } // 可选:对外暴露处理后的数值(忽略JSON序列化) [JsonIgnore] public double ProcessedValue => _Value; }
方案2:自定义JsonConverter处理object类型
若需保留object类型,可自定义转换器处理JSON对象的反序列化:
第一步:创建转换器
public class ObjectOrPrimitiveConverter : JsonConverter { public override bool CanConvert(Type objectType) { return objectType == typeof(object); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.StartObject) { // 将JSON对象转为JObject,方便后续判断 return JObject.Load(reader); } // 原始值直接返回 return serializer.Deserialize(reader); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { serializer.Serialize(writer, value); } }
第二步:在属性上标记使用转换器
[JsonObject] public class PiValue { private DateTime _Timestamp = DateTime.MinValue; private object _Value = 0d; [JsonProperty] public DateTime Timestamp { get { return _Timestamp; } set { _Timestamp = value.ToLocalTime(); } } [JsonProperty, JsonConverter(typeof(ObjectOrPrimitiveConverter))] public object Value { get { return _Value; } set { if (_Value != value) { double val = 0; if (value is null) _Value = double.NaN; else if (value is JObject) // 识别到嵌套对象,设为NaN _Value = double.NaN; else if (double.TryParse(value.ToString(), out val)) _Value = val; else _Value = double.NaN; } } } }
方案3:保留异常对象信息(如需业务处理)
若需要保留嵌套对象的详细信息,可定义对应实体类,通过JToken区分处理:
// 定义异常对象的实体类 public class ShutdownInfo { public string Name { get; set; } public int Value { get; set; } public bool IsSystem { get; set; } } [JsonObject] public class PiValue { private DateTime _Timestamp = DateTime.MinValue; private double _Value = 0d; private ShutdownInfo _ShutdownInfo; [JsonProperty] public DateTime Timestamp { get { return _Timestamp; } set { _Timestamp = value.ToLocalTime(); } } [JsonProperty] public JToken Value { set { if (value.Type == JTokenType.Object) { // 解析为异常对象 _ShutdownInfo = value.ToObject<ShutdownInfo>(); _Value = double.NaN; } else { _ShutdownInfo = null; _Value = value.ToObject<double?>() ?? double.NaN; } } } [JsonIgnore] public double ProcessedValue => _Value; [JsonIgnore] public ShutdownInfo ShutdownInfo => _ShutdownInfo; }
补充说明
- 为何改成
string类型仍报错?因为Newtonsoft.Json反序列化string类型时,仅支持JSON字符串原始值,遇到JSON对象会直接抛出异常,无法自动将对象转为字符串。 - 为何他人同版本能成功?可能对方配置了全局Json序列化设置(如
TypeNameHandling)、使用了自定义转换器,或实体类定义不同(如用JToken替代object)。
内容的提问来源于stack exchange,提问作者ErikJL
相关产品推荐
相关产品推荐

