如何反序列化50GB大型复杂JSON文件?附压缩包处理代码问题
问题描述
我有一个约50GB的JSON文件需要反序列化,该文件包含14个数组,已编写对应15个类的POCO文件(含根类)。尝试不解压完整压缩包直接处理数据,使用IO.Compression编写了C#代码,但代码在任务的foreach循环中失败,报错System.Threading.Tasks.VoidTaskResult。
代码如下:
using System.IO.Compression; using System.Text.Json; using System.Text.Json.Nodes; namespace read_and_parse { internal class Program { static void Main() { var fc = new Program(); string zip_path = @"C:\Projects\BBR\Download_Total\example_json.zip"; using FileStream file = File.OpenRead(zip_path); using (var zip = new ZipArchive(file, ZipArchiveMode.Read)) { foreach (ZipArchiveEntry entry in zip.Entries) { string[] name_split = entry.Name.Split('_'); string name = name_split.Last().Substring(0, name_split.Last().Length - 5); bool canConvert = long.TryParse(name, out long number1); if (canConvert == true) { Task task = fc.ParseJsonFromZippedFile(entry); } } } } private async Task ParseJsonFromZippedFile(ZipArchiveEntry entry) { JsonSerializerOptions options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; await using Stream entryStream = entry.Open(); IAsyncEnumerable<JsonNode?> enumerable = JsonSerializer.DeserializeAsyncEnumerable<JsonNode>(entryStream, options); await foreach (JsonNode? obj in enumerable) { // Parse only subset of the object JsonNode? bbrSagNode = obj?["BBRSaglist"]; if (bbrSagNode is null) continue; else { var bbrSag = bbrSagNode.Deserialize<BBRSagList>(); } } } } }
解决方案
1. 修复异步方法调用问题
Main方法中调用ParseJsonFromZippedFile时仅创建Task但未等待,导致程序可能在异步任务执行前就释放了ZipArchive等资源,引发错误。需将Main改为异步方法并等待任务:
static async Task Main() { var fc = new Program(); string zip_path = @"C:\Projects\BBR\Download_Total\example_json.zip"; using FileStream file = File.OpenRead(zip_path); using (var zip = new ZipArchive(file, ZipArchiveMode.Read)) { foreach (ZipArchiveEntry entry in zip.Entries) { string[] name_split = entry.Name.Split('_'); string name = name_split.Last().Substring(0, name_split.Last().Length - 5); if (long.TryParse(name, out _)) { await fc.ParseJsonFromZippedFile(entry); } } } }
2. 修正流式反序列化逻辑
DeserializeAsyncEnumerable<T>适用于多行JSON(每行一个独立JSON对象),但你的JSON是单个根对象结构,直接使用会导致解析失败。应直接反序列化到根POCO类,再按需处理目标数组:
假设根类名为RootObject,修改ParseJsonFromZippedFile方法:
private async Task ParseJsonFromZippedFile(ZipArchiveEntry entry) { JsonSerializerOptions options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; await using Stream entryStream = entry.Open(); // 直接反序列化到根类 var root = await JsonSerializer.DeserializeAsync<RootObject>(entryStream, options); if (root?.BBRSaglist != null) { // 处理BBRSaglist数组,可按需分批处理避免内存溢出 foreach (var item in root.BBRSaglist) { // 处理单个BBRSag项 } } }
3. 大文件内存优化(可选)
若根对象直接反序列化占用内存过高,可使用Utf8JsonReader进行低内存流式解析,仅提取BBRSaglist数组内容:
private async Task ParseJsonFromZippedFile(ZipArchiveEntry entry) { JsonSerializerOptions options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; await using Stream entryStream = entry.Open(); using var jsonReader = new Utf8JsonReader(new StreamReader(entryStream).BaseStream, leaveOpen: true); while (jsonReader.Read()) { // 定位到BBRSaglist数组的起始位置 if (jsonReader.TokenType == JsonTokenType.PropertyName && jsonReader.GetString() == "BBRSaglist") { jsonReader.Read(); // 跳过属性名,进入数组节点 if (jsonReader.TokenType == JsonTokenType.StartArray) { // 逐个解析数组元素 while (jsonReader.Read()) { if (jsonReader.TokenType == JsonTokenType.EndArray) break; var sagItem = JsonSerializer.Deserialize<BBRSag>(ref jsonReader, options); // 处理单个sagItem } } break; // 找到目标数组后停止遍历其他属性 } } }
内容的提问来源于stack exchange,提问作者TomGeo
相关产品推荐
相关产品推荐

