.NET 4.8下C#提取类JSON格式文本数据的更优方案咨询
优化C# .NET 4.8下类JSON格式文本的数据提取方案
问题背景
我正在学习C#,基于.NET 4.8框架开发,需要从类JSON格式的文本文件中提取指定数据(包括主名称、关键数字、路径、分类、布尔值及嵌套列表等),最终存储到List<ClassTech>集合中。当前的逐行硬匹配解析方案在数据规模增大后,维护难度骤增,希望找到更简洁高效的实现方式。
当前实现代码
TechData = new List<ClassTech>(); string[] paths = Directory.GetFiles(path, "*.txt"); foreach (string pathsl in paths) { using (StreamReader sr = File.OpenText(pathsl)) { string line; int i = 0; string[] words; while ((line = sr.ReadLine()) != null) { if (line.Contains("{") == true && !line.Contains("#")) { // 获取名称 words = line.Split('='); TechData.Add(new ClassTech(words[0].Trim(' '), "None", -1, null, "None", null, true)); // 处理{}内的内容 while ((line = sr.ReadLine()) != null) { if ((Regex.Match(line, @"\bera\b")).Success && (TechData[i].era == -1) && !line.Contains("#")) { words = line.Split('='); TechData[i].era = words[1][words[1].Length - 1] - 48; } else if ((Regex.Match(line, @"\btexture\b")).Success && (TechData[i].texture == null) && !line.Contains("#")) { words = line.Split('='); TechData[i].texture = words[1].Trim(' ', '"'); } else if ((Regex.Match(line, @"\bcategory\b")).Success && (TechData[i].category == null) && !line.Contains("#")) { words = line.Split('='); TechData[i].category = words[1].Trim(' '); } else if ((Regex.Match(line, @"\bcan_research\b")).Success && (Regex.Match(line, @"\bno\b")).Success && !line.Contains("#")) { TechData[i].canResearch = false; } else if ((Regex.Match(line, @"\bmodifier\b")).Success && !line.Contains("#")) { while ((line = sr.ReadLine()) != null) { if (line.Contains("}")) { break; } else if (line.Contains("=")) { TechData[i].modifiers.Add(line.Trim(' ')); } } } else if ((Regex.Match(line, @"\bunlocking_technologies\b")).Success && !line.Contains("#")) { while ((line = sr.ReadLine()) != null) { if (line.Contains("}")) { break; } else if (!string.IsNullOrEmpty(line) && !string.IsNullOrWhiteSpace(line)) { TechData[i].restrictions.Add(line.Trim('\t')); } } } else if (line.Contains("}") == true) { break; } } i++; } } } }
目标数据格式示例
sericulture = { # Unlocks Mulberry Groves PM on Rice Farms era = era_1 texture = "gfx/interface/icons/invention_icons/sericulture.dds" category = production can_research = no modifier = { building_silk_plantation_throughput_mult = 0.25 } } enclosure = { # Unlocks construction of Farms and Plantations era = era_1 texture = "gfx/interface/icons/invention_icons/enclosure.dds" category = production } manufacturies = { # Unlocks Mercantilism Law # Unlocks Food Industry, Textile Mills, Furniture Manufacturies, Glassworks, Tooling Workshops, Paper Mills era = era_1 texture = "gfx/interface/icons/invention_icons/manufacturies.dds" category = production } shaft_mining = { # Unlocks Coal Mine, Iron Mine, Lead Mine, Sulfur Mine era = era_1 texture = "gfx/interface/icons/invention_icons/shaft_mining.dds" category = production unlocking_technologies = { enclosure manufacturies } } atmospheric_engine = { # Unlocks Motor Industry # Unlocks Atmospheric Engine Pump PM in Coal Mine, Iron Mine, Lead Mine, Sulfur Mine era = era_1 texture = "gfx/interface/icons/invention_icons/atmospheric_engine.dds" category = production unlocking_technologies = { shaft_mining } }
优化实现方案
当前逐行硬匹配的代码扩展性极差,推荐两种更高效的思路:
方案1:利用现成的PDX脚本解析库
你处理的是Paradox游戏常用的PDX脚本格式,已有成熟的.NET解析库支持(适配.NET 4.8),这类库可以直接将文本解析为结构化对象,无需手动处理每一行。核心步骤:
- 引入适配.NET Framework的PDX脚本解析NuGet包
- 读取文本文件内容
- 调用库的解析方法将文本转为动态对象或强类型模型
- 直接映射到
ClassTech集合
这种方式完全省去手动解析的繁琐,后续新增字段也无需修改解析逻辑。
方案2:自定义模块化解析器
如果不想依赖第三方库,可以将解析逻辑拆分为三个独立模块,降低维护成本:
1. 文本预处理模块
先清理掉注释、空白行,统一格式:
private static IEnumerable<string> PreprocessLines(string filePath) { return File.ReadLines(filePath) .Select(line => line.Trim()) .Where(line => !string.IsNullOrWhiteSpace(line) && !line.StartsWith("#")); }
2. 递归解析模块
通过递归识别嵌套块,将整个文本解析为键值对字典:
private static Dictionary<string, object> ParseBlock(IEnumerator<string> lineEnumerator) { var block = new Dictionary<string, object>(); while (lineEnumerator.MoveNext()) { var line = lineEnumerator.Current; if (line == "}") break; if (line.Contains("=")) { var parts = line.Split(new[] { '=' }, 2); var key = parts[0].Trim(); var valuePart = parts[1].Trim(); if (valuePart == "{") { // 递归解析嵌套块 block[key] = ParseBlock(lineEnumerator); } else { // 处理普通值,去掉引号 block[key] = valuePart.Trim('"'); } } else { // 处理无值的项(比如unlocking_technologies里的条目) if (block.TryGetValue("__items", out var itemsObj) && itemsObj is List<string> items) { items.Add(line); } else { block["__items"] = new List<string> { line }; } } } return block; } // 顶级解析方法 private static List<Dictionary<string, object>> ParseTechFiles(string directoryPath) { var techBlocks = new List<Dictionary<string, object>>(); foreach (var filePath in Directory.GetFiles(directoryPath, "*.txt")) { var lines = PreprocessLines(filePath).GetEnumerator(); while (lines.MoveNext()) { var line = lines.Current; if (line.Contains("=") && line.EndsWith("{")) { var parts = line.Split(new[] { '=' }, 2); var techName = parts[0].Trim(); var techBlock = ParseBlock(lines); techBlock["Main_name"] = techName; techBlocks.Add(techBlock); } } } return techBlocks; }
3. 模型映射模块
将解析后的字典映射到ClassTech对象:
private static List<ClassTech> MapToClassTech(List<Dictionary<string, object>> techBlocks) { var techList = new List<ClassTech>(); foreach (var block in techBlocks) { var tech = new ClassTech { Main_name = block["Main_name"].ToString(), era = int.Parse(block["era"].ToString().Split('_')[1]), texture = block["texture"].ToString(), category = block["category"].ToString(), canResearch = !block.ContainsKey("can_research") || block["can_research"].ToString() != "no", modifiers = block.ContainsKey("modifier") && block["modifier"] is Dictionary<string, object> modifierBlock ? modifierBlock.Keys.Select(k => $"{k} = {modifierBlock[k]}").ToList() : new List<string>(), restrictions = block.ContainsKey("unlocking_technologies") && block["unlocking_technologies"] is Dictionary<string, object> unlockBlock ? unlockBlock["__items"] as List<string> : new List<string>() }; techList.Add(tech); } return techList; }
使用方式
var parsedBlocks = ParseTechFiles("你的目录路径"); TechData = MapToClassTech(parsedBlocks);
这种模块化的方式,后续新增字段只需要修改映射逻辑,解析逻辑无需改动,维护成本大幅降低。
内容的提问来源于stack exchange,提问作者Filipe
相关产品推荐
相关产品推荐

