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

如何在C#中使用System.Text.Json将属性序列化为独立JSON

实现拆分JSON序列化与延迟加载方案

1. 定义核心类结构

先调整类X,加入路径存储和懒加载逻辑,类A包含X类型的属性:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

public class X
{
    // 标记是否需要拆分存储
    public bool SplitJson { get; set; }
    // 大型列表数据
    public List<BigDataItem> LargeList { get; set; } = new();
    
    // 存储拆分后的文件路径(序列化/反序列化时忽略)
    [JsonIgnore]
    public string? FilePath { get; set; }
    
    // 懒加载逻辑:首次访问时才从文件加载数据
    private Lazy<List<BigDataItem>>? _lazyLargeList;
    [JsonIgnore]
    public List<BigDataItem> LazyLargeList
    {
        get
        {
            if (_lazyLargeList != null)
                return _lazyLargeList.Value;
            
            return LargeList;
        }
    }

    // 初始化懒加载逻辑(反序列化路径时调用)
    internal void InitializeLazyLoad()
    {
        if (!string.IsNullOrEmpty(FilePath))
        {
            _lazyLargeList = new Lazy<List<BigDataItem>>(() =>
            {
                var json = File.ReadAllText(FilePath);
                return JsonConvert.DeserializeObject<List<BigDataItem>>(json) ?? new List<BigDataItem>();
            });
            // 清空内存列表,强制使用懒加载版本
            LargeList = null!;
        }
    }
}

public class BigDataItem
{
    // 示例大型数据项属性
    public int Id { get; set; }
    public string Content { get; set; } = string.Empty;
}

public class A
{
    [JsonConverter(typeof(XSplitConverter))]
    public X? X1 { get; set; }
    
    [JsonConverter(typeof(XSplitConverter))]
    public X? X2 { get; set; }
    
    [JsonConverter(typeof(XSplitConverter))]
    public X? X3 { get; set; }
}

2. 自定义JSON转换器实现拆分逻辑

编写XSplitConverter处理X类型的序列化/反序列化,同时管理拆分文件的存储:

public class XSplitConverter : JsonConverter<X>
{
    // 拆分文件的基础存储目录
    private readonly string _baseDirectory;

    public XSplitConverter() : this(Directory.GetCurrentDirectory()) { }
    public XSplitConverter(string baseDirectory)
    {
        _baseDirectory = baseDirectory;
        Directory.CreateDirectory(_baseDirectory); // 确保目录存在
    }

    public override void WriteJson(JsonWriter writer, X? value, JsonSerializer serializer)
    {
        if (value == null)
        {
            writer.WriteNull();
            return;
        }

        if (value.SplitJson)
        {
            // 生成唯一文件名(可根据需求替换为更具可读性的命名规则)
            var fileName = $"X_{Guid.NewGuid()}.json";
            var fullPath = Path.Combine(_baseDirectory, fileName);
            
            // 将X的大型列表单独序列化到文件
            var listJson = JsonConvert.SerializeObject(value.LargeList, Formatting.Indented);
            File.WriteAllText(fullPath, listJson);
            
            // 序列化主文件时写入相对路径
            writer.WriteValue(Path.GetRelativePath(_baseDirectory, fullPath));
            // 保存路径到X实例(用于后续操作)
            value.FilePath = fullPath;
        }
        else
        {
            // 无需拆分时正常序列化整个X对象
            serializer.Serialize(writer, value);
        }
    }

    public override X? ReadJson(JsonReader reader, Type objectType, X? existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        // 判断当前值是路径字符串还是完整X对象
        if (reader.TokenType == JsonToken.String)
        {
            var relativePath = reader.Value.ToString();
            var fullPath = Path.Combine(_baseDirectory, relativePath);
            
            var x = new X
            {
                SplitJson = true,
                FilePath = fullPath
            };
            x.InitializeLazyLoad(); // 初始化懒加载
            return x;
        }
        else
        {
            // 正常反序列化X对象
            var x = serializer.Deserialize<X>(reader);
            x?.InitializeLazyLoad(); // 兼容非拆分场景
            return x;
        }
    }
}

3. 使用示例

序列化类A

// 构造测试数据
var a = new A
{
    X1 = new X { SplitJson = true, LargeList = Enumerable.Range(1, 10000).Select(i => new BigDataItem { Id = i, Content = $"Item {i}" }).ToList() },
    X2 = new X { SplitJson = false, LargeList = Enumerable.Range(1, 100).Select(i => new BigDataItem { Id = i, Content = $"Small Item {i}" }).ToList() },
    X3 = null
};

// 序列化A到主文件
var serializerSettings = new JsonSerializerSettings
{
    Converters = { new XSplitConverter(@"C:\SplitJsonStorage") }, // 指定存储目录
    Formatting = Formatting.Indented
};
var aJson = JsonConvert.SerializeObject(a, serializerSettings);
File.WriteAllText(@"C:\SplitJsonStorage\A.json", aJson);

反序列化类A并延迟加载

// 反序列化主文件
var deserializedA = JsonConvert.DeserializeObject<A>(File.ReadAllText(@"C:\SplitJsonStorage\A.json"), serializerSettings);

// 访问X1的LazyLargeList时才会加载对应拆分文件
var x1Items = deserializedA?.X1?.LazyLargeList;
Console.WriteLine($"X1加载了 {x1Items?.Count} 条数据");

// X2未拆分,直接读取内存数据
var x2Items = deserializedA?.X2?.LazyLargeList;
Console.WriteLine($"X2加载了 {x2Items?.Count} 条数据");

关键注意事项

  • 路径一致性:序列化和反序列化必须使用相同的基础目录,避免路径解析错误。
  • 线程安全:默认Lazy<T>是线程安全的,若需自定义线程行为可调整LazyThreadSafetyMode参数。
  • 异常处理:实际生产环境中需添加文件读写、JSON反序列化的异常捕获逻辑,防止程序崩溃。
  • 文件名规则:示例用Guid生成唯一文件名,也可根据类A的标识、属性名等生成更易识别的文件名。

内容的提问来源于stack exchange,提问作者ShankarBUS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 14:07:38