基于Newtonsoft.Json实现JSON字符串的条件反序列化优化
解决方案:流式读取JSON,仅反序列化符合条件的项
刚好之前处理过类似的场景——当缓存里存了几千条记录的JSON数组时,全量反序列化再做Linq查询确实会造成不必要的性能浪费。好在Newtonsoft.Json(Json.NET)提供了底层流式读取API,能让我们逐个扫描JSON节点,只处理符合筛选条件的对象,完全不用把整个数组加载到内存中。
方法1:根据单个条件获取匹配的单个对象
比如你要找Code为"DUPLICATE_CHECK"的记录,可以用下面的方法:
public static SomeClass GetSingleItemByCode(string jsonCache, string targetCode) { if (string.IsNullOrEmpty(jsonCache)) return null; // 用StringReader和JsonTextReader流式读取JSON using (var stringReader = new StringReader(jsonCache)) using (var jsonReader = new JsonTextReader(stringReader)) { // 跳过数组开头的[符号(如果当前没指向StartArray的话) if (jsonReader.TokenType != JsonToken.StartArray) jsonReader.Read(); // 遍历数组中的每个元素 while (jsonReader.Read()) { // 遇到对象开始的节点时处理 if (jsonReader.TokenType == JsonToken.StartObject) { // 加载当前对象为JObject(仅加载这一个对象,不是整个数组) var jObject = JObject.Load(jsonReader); var codeValue = jObject["Code"]?.ToString(); // 匹配到目标Code时,反序列化为SomeClass并返回 if (codeValue == targetCode) { return jObject.ToObject<SomeClass>(); } } } } // 没找到匹配项返回null return null; }
方法2:根据条件获取多个匹配对象
如果需要获取多个符合条件的记录(比如所有Type为"0"的项),可以稍作修改:
public static List<SomeClass> GetItemsByType(string jsonCache, string targetType) { var results = new List<SomeClass>(); if (string.IsNullOrEmpty(jsonCache)) return results; using (var stringReader = new StringReader(jsonCache)) using (var jsonReader = new JsonTextReader(stringReader)) { if (jsonReader.TokenType != JsonToken.StartArray) jsonReader.Read(); while (jsonReader.Read()) { if (jsonReader.TokenType == JsonToken.StartObject) { var jObject = JObject.Load(jsonReader); var typeValue = jObject["Type"]?.ToString(); if (typeValue == targetType) { results.Add(jObject.ToObject<SomeClass>()); } } } } return results; }
方法3:通用筛选方法(支持自定义条件)
如果需要更灵活的筛选逻辑,可以把筛选条件做成委托,适配各种查询需求:
public static List<SomeClass> FilterJsonItems(string jsonCache, Func<JObject, bool> filter) { var results = new List<SomeClass>(); if (string.IsNullOrEmpty(jsonCache) || filter == null) return results; using (var stringReader = new StringReader(jsonCache)) using (var jsonReader = new JsonTextReader(stringReader)) { if (jsonReader.TokenType != JsonToken.StartArray) jsonReader.Read(); while (jsonReader.Read()) { if (jsonReader.TokenType == JsonToken.StartObject) { var jObject = JObject.Load(jsonReader); // 用自定义筛选条件判断是否保留当前对象 if (filter(jObject)) { results.Add(jObject.ToObject<SomeClass>()); } } } } return results; }
调用示例:
// 找Code为DUPLICATE_CHECK的第一个项 var duplicateItem = FilterJsonItems(cacheJson, j => j["Code"]?.ToString() == "DUPLICATE_CHECK").FirstOrDefault(); // 找所有Type为0且IsActive为Y的项 var activeType0Items = FilterJsonItems(cacheJson, j => j["Type"]?.ToString() == "0" && j["IsActive"]?.ToString() == "Y");
核心优势
- 极低内存占用:每次仅处理单个JSON对象,不会把整个几千条记录的数组加载到内存中
- 更快的查询速度:找到匹配项后可以立即返回(比如方法1),不需要遍历完整个数组
- 兼容性强:
JObject.ToObject<SomeClass>()和你原来用的JsonConvert.DeserializeObject<T>效果完全一致,支持复杂嵌套属性
注意事项
- 确保缓存中的JSON是合法的数组格式,如果格式可能异常,可以在方法中添加
try-catch块处理 JObject.Load(jsonReader)会自动消耗当前的StartObject节点,不需要额外调用jsonReader.Read(),否则会跳过下一个节点
内容的提问来源于stack exchange,提问作者Neeraj Kumar Gupta
相关产品推荐
相关产品推荐

