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

如何正确将格式异常的JSON字符串反序列化为C#类?

问题描述

给定如下JSON字符串:

{ 
    "number": "1", 
    "optionsList": [ 
        "{\"answer\":\"42\", \"options\":\"washington\"}", 
        "{\"answer\":\"65\", \"options\":\"new england\"}" 
    ] 
}

其中optionsList的每个元素是JSON字符串而非JSON对象,无法修改原始JSON,需要将其正确反序列化为已定义的C#类:

Root.cs

public class Root { 
    public Root() {
        Number = ""; 
        OptionsList = new List<OptionsList>();
    }

    [JsonProperty(PropertyName = "number")]
    public string Number { get; internal set; }

    [JsonProperty(PropertyName = "optionsList")]
    public List<OptionsList> OptionsList { get; internal set; }
} 

OptionsList.cs

public class OptionsList { 
    [JsonProperty(PropertyName = "answer")] 
    public string Answer { get; internal set; }

    [JsonProperty(PropertyName = "options")]
    public string Options { get; internal set; }
} 

使用JsonConvert.DeserializeObject<T>(response)反序列化时抛出异常:

---&gt; Newtonsoft.Json.JsonSerializationException: Error converting value "{\"answer\":\"42\",\"options\":\"washington\"}" to type 'Types.OptionsList'. Path 'OptionsList[0]', line 1, position 1947.
---&gt; System.ArgumentException: Could not cast or convert from System.String to Types.OptionsList.
   at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable(Object value, Type initialType, Type targetType)
   at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(Object initialValue, CultureInfo culture, Type targetType)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType)
   --- End of inner exception stack trace ---
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
   at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
   at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonConverter[] converters)
   at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonConverter[] converters)
解决方案

方法1:自定义JsonConverter处理嵌套JSON字符串

这是最优雅的解决方案,给OptionsList类型创建自定义转换器,自动将JSON字符串反序列化为对象:

public class NestedJsonConverter : JsonConverter<OptionsList>
{
    public override OptionsList ReadJson(JsonReader reader, Type objectType, OptionsList existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        // 读取原始JSON字符串并反序列化为OptionsList对象
        var jsonString = reader.Value.ToString();
        return JsonConvert.DeserializeObject<OptionsList>(jsonString);
    }

    public override void WriteJson(JsonWriter writer, OptionsList value, JsonSerializer serializer)
    {
        // 若需要序列化回JSON,将对象转为JSON字符串输出
        var jsonString = JsonConvert.SerializeObject(value);
        writer.WriteValue(jsonString);
    }
}

方式A:反序列化时传入转换器

var settings = new JsonSerializerSettings();
settings.Converters.Add(new NestedJsonConverter());
var deserializedResponse = JsonConvert.DeserializeObject<Root>(response, settings);

方式B:在类上标记特性(更简洁)

修改OptionsList类,直接绑定转换器:

[JsonConverter(typeof(NestedJsonConverter))]
public class OptionsList { 
    [JsonProperty(PropertyName = "answer")] 
    public string Answer { get; internal set; }

    [JsonProperty(PropertyName = "options")]
    public string Options { get; internal set; }
} 

之后直接调用原反序列化代码即可:

var deserializedResponse = JsonConvert.DeserializeObject<Root>(response);

方法2:先反序列化为中间类再转换

如果不想编写转换器,可以定义中间类接收原始结构,再手动转换为目标类型:

// 中间类,匹配原始JSON的结构
public class RootIntermediate { 
    [JsonProperty(PropertyName = "number")]
    public string Number { get; set; }

    [JsonProperty(PropertyName = "optionsList")]
    public List<string> OptionsList { get; set; }
} 

分两步处理:

// 第一步:反序列化为中间类
var intermediate = JsonConvert.DeserializeObject<RootIntermediate>(response);

// 第二步:转换为目标Root类
var result = new Root {
    Number = intermediate.Number,
    OptionsList = intermediate.OptionsList
        .Select(jsonStr => JsonConvert.DeserializeObject<OptionsList>(jsonStr))
        .ToList()
};

方法3:在Root类中处理(不推荐)

这种方式会破坏类的单一职责,且需要处理反序列化顺序问题,仅作为备选方案:

修改Root类的OptionsList属性,在setter中手动转换,但需要注意Json.NET会先尝试将字符串转为OptionsList,仍会触发异常,因此需要配合中间转换逻辑,不如前两种方案可靠。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 11:56:36