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

C#反序列化含未知字段及类型的JSON字符串时遇ThreadAbortException问题求助

问题分析与解决方案

嘿,我帮你拆解下你遇到的问题——核心是自定义JsonConverter的逻辑和输入的JSON格式不匹配,再加上多线程环境下的资源访问问题,才引发了ThreadAbortException。咱们一步步来解决:

1. 先揪出JSON格式与Converter的不匹配问题

你的输入是数组结构的JSON:

["Nameofinstruction",{"dx":"4000"},{"dy":"160"},{"dz":"120"},{"mat":"test_material"}]

但你的InstructionConverter是针对ArrivingMessage(一个对象类型)设计的,CanConvert返回的是objectType == typeof(ArrivingMessage),这会让Json.NET在处理数组时完全摸不着头脑,直接打乱JsonReader的读取流程,进而抛出异常,甚至触发线程中止。

另外,你原来的ReadJson逻辑也有坑:

  • 你直接在循环里调用reader.ReadAsString(),但根本没判断当前Token的类型(第一个元素是字符串,后面是对象)
  • reader.Path在数组遍历中会变成[1].dx这种格式,而不是你想要的dx,导致字典的键完全不符合预期

2. 多线程与线程安全的坑

ThreadAbortException一般意味着线程被强制中止,大概率是反序列化过程中抛出了未处理的异常,才触发了线程终止。另外还要注意:

  • 普通的Dictionary<string, dynamic>不是线程安全的,如果多个线程同时操作同一个ArrivingMessage实例的arguments字典,肯定会出问题
  • 一定要保证每个线程处理的是独立的JsonReader和ArrivingMessage实例,绝对不能跨线程共享这些资源

修正后的完整实现

第一步:调整模型类

先给ArrivingMessage加个InstructionName属性,用来存数组第一个元素的指令名,这样逻辑更清晰:

public class InstructionProc
{
    [JsonConverter(typeof(InstructionConverter))]
    public class ArrivingMessage
    {
        public ArrivingMessage()
        {
            Arguments = new Dictionary<string, dynamic>();
        }
        public string InstructionName { get; set; }
        public Dictionary<string, dynamic> Arguments { get; set; }
    }
    // 下面是Converter的重写...
}

第二步:重写JsonConverter的逻辑

针对输入的数组格式,重新实现ReadJson和WriteJson,确保能正确解析数组结构:

public class InstructionConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ArrivingMessage);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var message = value as ArrivingMessage;
        if (message == null) return;

        // 按照输入的数组格式序列化
        writer.WriteStartArray();
        // 第一个元素:指令名
        writer.WriteValue(message.InstructionName);
        // 后续元素:每个键值对单独封装成对象
        foreach (var kvp in message.Arguments)
        {
            writer.WriteStartObject();
            writer.WritePropertyName(kvp.Key);
            serializer.Serialize(writer, kvp.Value);
            writer.WriteEndObject();
        }
        writer.WriteEndArray();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var message = existingValue as ArrivingMessage ?? new ArrivingMessage();

        // 先确认当前是数组的起始位置
        if (reader.TokenType != JsonToken.StartArray)
            throw new JsonSerializationException("输入JSON必须是数组格式");

        // 读取第一个元素:指令名称
        reader.Read();
        if (reader.TokenType == JsonToken.String)
        {
            message.InstructionName = reader.Value.ToString();
        }
        else
        {
            throw new JsonSerializationException("数组第一个元素必须是指令名称字符串");
        }

        // 遍历数组剩余的键值对对象
        while (reader.Read())
        {
            if (reader.TokenType == JsonToken.EndArray)
                break;

            if (reader.TokenType != JsonToken.StartObject)
                throw new JsonSerializationException("数组后续元素必须是单个键值对的对象");

            // 读取对象的键
            reader.Read();
            if (reader.TokenType != JsonToken.PropertyName)
                throw new JsonSerializationException("对象必须包含属性名");

            string key = reader.Value.ToString();
            // 读取对应的值
            reader.Read();
            dynamic value = serializer.Deserialize(reader, typeof(dynamic));
            message.Arguments[key] = value;

            // 跳过对象结束标记
            reader.Read();
        }

        return message;
    }
}

第三步:多线程环境下的注意事项

  • 不要共享实例:每个客户端连接的线程都要创建自己的ArrivingMessage实例,绝对不能让多个线程同时修改同一个字典
  • 独立使用JsonReader:每个反序列化操作都要用独立的JsonReader,别在多个线程间共享这个资源
  • 线程安全字典(可选):如果确实需要多个线程访问同一个字典,把Dictionary换成ConcurrentDictionary<string, dynamic>,这样不用手动加锁也能保证线程安全

测试一下

用你的示例JSON测试反序列化:

string json = @"[""Nameofinstruction"",{""dx"":""4000""},{""dy"":""160""},{""dz"":""120""},{""mat"":""test_material""}]";
var message = JsonConvert.DeserializeObject<InstructionProc.ArrivingMessage>(json);

// 结果:
// message.InstructionName = "Nameofinstruction"
// message.Arguments 包含 {"dx":"4000", "dy":"160", "dz":"120", "mat":"test_material"}

这样应该能解决你之前的反序列化逻辑错误,同时避免多线程环境下的线程安全问题,ThreadAbortException也会消失啦。

内容的提问来源于stack exchange,提问作者Ángel Martínez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 21:57:42