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
相关产品推荐
相关产品推荐

