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

C#应用JSON数据源CRUD及写入:如何避免全文件重写?

关于JSON文件局部写入与C# CRUD操作的解决方案

嘿,我来帮你理清这个JSON文件操作的困惑!咱们先逐个解决你的问题:

问题1:能否在不重写整个文件的情况下写入JSON数据文件?

答案是分场景可行,但有很大局限性:

  • 如果你的JSON根结构是数组,且仅需要在数组末尾追加元素,那可以通过定位文件指针、修改末尾内容实现局部写入(不用全量加载)。但这种方式要求文件格式严格规范,且无法处理中间元素的修改或删除。
  • 对于其他场景(比如修改数组中间元素、更新嵌套对象属性),由于JSON是结构化的文本格式,没有内置的索引或偏移机制,局部修改会破坏整个文件的结构,所以几乎不可能实现——你要么全量加载修改后重写,要么用流式读写的方式“边读边写”,避免把整个文件塞进内存。

问题2:C#中避免全量序列化重写的JSON CRUD方案

针对你提到的小型C#应用,我给你两种实用方案,根据你的场景选择:

方案1:数组末尾快速追加(仅适用于根为数组的场景)

如果你的JSON文件是用户数组(比如[{"id":1}, {"id":2}]),可以直接操作文件流追加元素,不用全量序列化整个列表:

using System.IO;
using Newtonsoft.Json;

// 假设JsonEntry是你的自定义类
var newUser = new JsonEntry { Id = 3, Name = "Alice" };
var serializedUser = JsonConvert.SerializeObject(newUser);

using (var fs = new FileStream("users.json", FileMode.Open, FileAccess.ReadWrite))
{
    // 定位到文件倒数第二个字符(跳过末尾的"]")
    fs.Seek(-1, SeekOrigin.End);
    // 处理可能存在的换行符(如果文件末尾有换行,需要再往前偏移)
    var buffer = new byte[1];
    fs.Read(buffer, 0, 1);
    if (buffer[0] == '\n' || buffer[0] == '\r')
    {
        fs.Seek(-2, SeekOrigin.End);
        // 再检查一次回车换行
        fs.Read(buffer, 0, 1);
        if (buffer[0] == '\r')
        {
            fs.Seek(-1, SeekOrigin.Current);
        }
    }
    // 写入分隔符、新元素和闭合括号
    var appendContent = $", {serializedUser}]";
    var bytes = System.Text.Encoding.UTF8.GetBytes(appendContent);
    fs.Write(bytes, 0, bytes.Length);
}

⚠️ 注意:这个方法有坑——必须保证文件格式完全正确,且不能有并发写入,否则会直接破坏JSON结构。

方案2:流式读写处理任意CRUD操作(适合大文件或修改/删除场景)

如果需要修改或删除元素,推荐用流式读写:一边读取原文件的JSON令牌,一边写入到临时文件,遇到目标元素时修改,其他内容直接复制。这样不用把整个JSON加载到内存,内存占用极低。

用Newtonsoft.Json实现:

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

var tempFilePath = Path.GetTempFileName();

using (var reader = new JsonTextReader(new StreamReader("users.json")))
using (var writer = new JsonTextWriter(new StreamWriter(tempFilePath)))
{
    writer.Formatting = Formatting.Indented; // 保持原格式缩进
    while (reader.Read())
    {
        // 遇到对象开始令牌时,加载对象并检查是否需要修改
        if (reader.TokenType == JsonToken.StartObject)
        {
            var userObj = JObject.Load(reader);
            // 假设要修改id=2的用户名称
            if ((int)userObj["id"] == 2)
            {
                userObj["name"] = "Bob Updated";
            }
            userObj.WriteTo(writer);
        }
        // 其他令牌(比如数组开始、逗号等)直接复制
        else
        {
            writer.WriteToken(reader);
        }
    }
}

// 替换原文件(可以加个备份步骤更安全)
File.Delete("users.json");
File.Move(tempFilePath, "users.json");

用官方System.Text.Json实现(.NET Core 3.0+):

using System.IO;
using System.Text.Json;

var tempFilePath = Path.GetTempFileName();

using var inputStream = File.OpenRead("users.json");
using var outputStream = File.Create(tempFilePath);
using var reader = new Utf8JsonReader(inputStream);
using var writer = new Utf8JsonWriter(outputStream, new JsonWriterOptions { Indented = true });

while (reader.Read())
{
    if (reader.TokenType == JsonTokenType.StartObject)
    {
        // 从当前位置读取整个对象
        using var objectReader = reader.ReadFromCurrentPosition();
        var userElement = JsonElement.ParseValue(ref objectReader);
        
        // 检查是否是要修改的用户
        if (userElement.TryGetProperty("id", out var idElement) && idElement.GetInt32() == 2)
        {
            writer.WriteStartObject();
            // 遍历对象属性,修改目标属性
            foreach (var property in userElement.EnumerateObject())
            {
                if (property.Name == "name")
                {
                    writer.WriteString("name", "Bob Updated with System.Text.Json");
                }
                else
                {
                    property.WriteTo(writer);
                }
            }
            writer.WriteEndObject();
        }
        else
        {
            // 直接写入原对象
            userElement.WriteTo(writer);
        }
    }
    else
    {
        // 复制其他令牌
        writer.WriteToken(ref reader);
    }
}

// 替换原文件
File.Delete("users.json");
File.Move(tempFilePath, "users.json");

额外建议

如果你的应用需要频繁做CRUD操作,JSON文件其实不是最优选择——它没有内置的事务、索引和并发支持,容易出问题。如果可以的话,换成轻量数据库比如SQLite(支持JSON列),或者嵌入式数据库会更省心。但如果必须用JSON文件,上面的流式方案可以完美解决你的全量操作困扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:43:15