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

如何调整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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 04:31:02