如何调整NewtonSoft.Json的Deserialize()实现重复对象复用以节省内存?
问题:NewtonSoft.Json反序列化时复用重复对象引用以节省内存
我有一个包含大量重复对象的JSON文件,使用NewtonSoft.Json反序列化后,内容完全相同的对象引用却不相等。虽然功能不受影响,但受限于内存配额,我需要让相同的对象拥有相同的引用。
当前使用的反序列化代码如下:
using (StreamReader file = File.OpenText("filePath")) using (JsonTextReader reader = new JsonTextReader(file)) { reader.SupportMultipleContent = true; var serializer = new JsonSerializer(); var result = serializer.Deserialize<T>(reader); // 此操作后内存被占满 return result; }
补充信息
类结构代码:
public class Data { public int Version; public List<Office> Offices; } public class Office { public int Id; public List<Mark> Marks; } public class Mark { public string Name; }
示例JSON数据:
{ "version": 1, "offices": [ { "id": 1, "marks": [ { "name": "white" }, { "name": "blue" } ] }, { "id": 2, "marks": [ { "name": "white" }, { "name": "green" } ] }, { "id": 3, "marks": [ { "name": "white" }, { "name": "blue" } ] } ] }
由于子对象被包装在父对象中,无法逐个处理offices对象,需要调整Deserialize()实现对象引用复用。
解决方案
可以通过自定义ReferenceResolver并配合JsonSerializer的配置来实现重复对象的引用复用,核心思路是在反序列化过程中缓存已创建的对象,遇到相同标识的对象时直接返回缓存引用。
1. 自定义对象缓存解析器
创建一个继承自DefaultReferenceResolver的解析器,针对目标类型(比如示例中的Mark),根据对象的唯一标识字段(比如Name)缓存对象:
public class ObjectCacheResolver : DefaultReferenceResolver { private readonly Dictionary<string, object> _objectCache = new Dictionary<string, object>(); private readonly Type _targetType; private readonly Func<object, string> _getUniqueKey; public ObjectCacheResolver(Type targetType, Func<object, string> getUniqueKey) { _targetType = targetType; _getUniqueKey = getUniqueKey; } public override object ResolveReference(object context, string reference) { // 尝试从缓存中获取已存在的对象 if (_objectCache.TryGetValue(reference, out var cachedObj)) { return cachedObj; } return base.ResolveReference(context, reference); } public override string GetReference(object context, object value) { // 仅对目标类型进行缓存处理 if (value.GetType() == _targetType) { var uniqueKey = _getUniqueKey(value); // 若缓存中无此对象,则存入缓存 if (!_objectCache.ContainsKey(uniqueKey)) { _objectCache[uniqueKey] = value; } return uniqueKey; } return base.GetReference(context, value); } }
2. 修改反序列化代码配置
在创建JsonSerializer时,指定自定义解析器,并开启引用处理开关:
using (StreamReader file = File.OpenText("filePath")) using (JsonTextReader reader = new JsonTextReader(file)) { reader.SupportMultipleContent = true; var serializer = new JsonSerializer(); // 配置解析器:针对Mark类型,以Name作为唯一标识缓存对象 serializer.ReferenceResolver = new ObjectCacheResolver( typeof(Mark), mark => ((Mark)mark).Name ); // 开启对象引用保留,确保解析器生效 serializer.PreserveReferencesHandling = PreserveReferencesHandling.Objects; var result = serializer.Deserialize<T>(reader); return result; }
说明
- 该方案会在反序列化
Mark对象时,自动复用相同Name的对象引用,大幅减少重复对象占用的内存。 - 如果需要复用其他类型的对象,只需调整
ObjectCacheResolver的目标类型和唯一标识生成逻辑(比如多字段组合生成唯一键)。 - 唯一标识的生成逻辑必须准确,确保业务上相同的对象能生成完全一致的键,避免错误复用或无法复用的情况。
内容的提问来源于stack exchange,提问作者nozd
相关产品推荐
相关产品推荐

