` 将 CSV 行转换为动态对象)
后端数据工程【免费下载链接】CsvHelperLibrary to help reading and writing CSV files项目地址https://gitcode.com/gh_mirrors/cs/CsvHelper点击查看免费下载导读当 CSV 的列结构在编译期未知、或者你希望跳过为每张表定义 POCO 类的步骤时CsvHelper 提供了GetRecordsdynamic()这一动态记录读取方案它把每一行 CSV 数据转换为运行时动态对象属性名直接取自表头。读完本文你将掌握动态记录的基本用法、属性值的类型规则全部为字符串、无表头文件的字段命名规则以及如何通过GetDynamicPropertyName、PrepareHeaderForMatch等配置扩展动态对象的命名行为并理解其底层基于FastDynamicObject的实现原理。本文关联的原文档为 get-dynamic-records属于 CsvHelper 网站 reading 示例系列 的一部分。一、核心概念CSV 行与dynamic对象CsvHelper 支持把 CSV 行转换为多种记录形式强类型类、匿名类型、dynamic动态对象。其中动态对象适用于列结构不确定或需要通用处理的场景。关键约定来自原文档每一行 CSV 数据都会被转换为一个dynamic对象由于编译期无法获知属性的真实类型动态对象上的所有属性值一律是字符串。也就是说即使 CSV 单元格里写的是1通过dynamic读取得到的也是字符串1不会自动转换为int。如果需要类型转换应当使用强类型类配合类型转换器参见 type-conversion 系列。二、最简示例把 CSV 行读成动态对象原文档给出了如下最小可运行示例。假设磁盘上有文件file.csv内容为Id,Name 1,one读取代码void Main() { using (var reader new StreamReader(path\\to\\file.csv)) using (var csv new CsvReader(reader, CultureInfo.InvariantCulture)) { var records csv.GetRecordsdynamic(); } }随后可以像访问普通对象那样访问每一行foreach (dynamic record in records) { Console.WriteLine(record.Id); // 输出: 1 Console.WriteLine(record.Name); // 输出: one }要点说明CultureInfo.InvariantCulture作为配置传入CsvReader这是 CsvHelper 文档示例中的标准写法用于避免区域性设置影响解析using同时包裹StreamReader与CsvReader确保资源释放records是IEnumerabledynamic支持foreach直接遍历。三、惰性求值GetRecordsdynamic()的延迟执行特性GetRecordsT()返回的是按需生成的迭代器方法体只有在真正访问记录如调用.ToList()或foreach时才执行。这一点在源码注释中明确警告CsvReader.csGetRecordsT()returns an IEnumerable that yields records. This means that the method isnt actually called until you try and access the values. e.g..ToList()由此带来的实际影响是如果你在using块内创建CsvReader却在using块之外去遍历records会抛出ObjectDisposedException——因为迭代发生时 reader 已被释放。正确做法是在using块内部完成消费using (var csv new CsvReader(reader, CultureInfo.InvariantCulture)) { var records csv.GetRecordsdynamic().ToList(); // 在 using 内完成求值 // 之后可以在块外使用 records }另外从源码可见CsvReader.csGetRecordsT()会在读取记录前自动消费表头当配置开启表头记录hasHeaderRecord且尚未读取时先调用Read()与ReadHeader()并执行ValidateHeaderT()。因此使用GetRecordsdynamic()时无需手动先调用ReadHeader()。四、源码视角动态记录是如何创建的理解底层实现有助于把握动态对象的行为边界。CsvHelper 通过表达式工厂按记录类型选择不同的创建器RecordCreatorFactory.cs类型为基元类型primitive时使用PrimitiveRecordCreator类型为typeof(object)时使用DynamicRecordCreator——而dynamic在运行时正是以object传递的所以GetRecordsdynamic()走的是这一分支其余类型使用ObjectRecordCreator。DynamicRecordCreator.CreateDynamicRecord()的具体逻辑DynamicRecordCreator.csprotected virtual dynamic CreateDynamicRecord() { var obj new FastDynamicObject(); var dict obj as IDictionarystring, object?; if (Reader.HeaderRecord ! null) { for (var i 0; i Reader.HeaderRecord.Length; i) { var args new GetDynamicPropertyNameArgs(i, Reader.Context); var propertyName Reader.Configuration.GetDynamicPropertyName(args); Reader.TryGetField(i, out string? field); dict[propertyName] field; } } else { for (var i 0; i Reader.Parser.Count; i) { var args new GetDynamicPropertyNameArgs(i, Reader.Context); var propertyName Reader.Configuration.GetDynamicPropertyName(args); var field Reader.GetField(i); dict[propertyName] field; } } return obj; }从中可以确认三个事实属性名来源于表头有表头时按表头列名经过配置函数处理后作为动态属性名字段值直接取自当前行对应索引值始终为字符串代码中使用out string? field与GetField(i)没有任何类型转换印证了原文档所有属性都是字符串的约定无表头时按序号命名没有表头记录时使用GetDynamicPropertyName的默认实现生成Field1、Field2、Field3…… 依次递增见下文。动态对象本体是FastDynamicObjectFastDynamicObject.cs它同时实现了IDynamicMetaObjectProvider与IDictionarystring, object?前者通过DynamicMetaObject绑定实现record.Id这样的动态成员访问后者使动态对象可以被当作字典遍历。二者组合保证了既能按属性访问、也能按集合处理的灵活性。五、属性命名规则的定制动态属性的命名不是写死的而是由配置委托GetDynamicPropertyName控制CsvConfiguration.cs委托签名见 GetDynamicPropertyName.cs。默认实现位于 ConfigurationFunctions.cspublic static string GetDynamicPropertyName(GetDynamicPropertyNameArgs args) { if (args.Context.Reader?.HeaderRecord null) { return $Field{args.FieldIndex 1}; } var header args.Context.Reader.HeaderRecord[args.FieldIndex]; var prepareHeaderForMatchArgs new PrepareHeaderForMatchArgs(header, args.FieldIndex); header args.Context.Reader.Configuration.PrepareHeaderForMatch(prepareHeaderForMatchArgs); return header; }即默认规则是有表头时属性名 表头经PrepareHeaderForMatch处理后的结果无表头时属性名 Field1、Field2……索引从 1 开始。利用这一点可以衍生出三种实用定制5.1 通过PrepareHeaderForMatch清理表头字符表头中若含空格等不便作为属性名的字符可统一清洗。测试用例 DynamicTests.cs 展示了将O ne,Tw o,Thr ee去掉空格后得到records[0].One、.Two、.Three的验证var config new CsvConfiguration(CultureInfo.InvariantCulture) { PrepareHeaderForMatch args args.Header.Replace( , string.Empty), }; var records csv.GetRecordsdynamic().ToList(); Assert.Equal(1, records[0].One); Assert.Equal(2, records[0].Two); Assert.Equal(3, records[0].Three);5.2 为空白表头生成占位属性名表头可能为空列默认实现下属性名会变成空字符串访问不便。可通过PrepareHeaderForMatch为空白表头回退生成Blank{index}见 DynamicTests.csPrepareHeaderForMatch args { if (string.IsNullOrWhiteSpace(args.Header)) { return $Blank{args.FieldIndex}; } return args.Header; },5.3 通过GetDynamicPropertyName处理重复列名当 CSV 中存在重复表头如两个Name列时默认属性名会冲突。测试用例 DynamicTests.cs 展示了结合GetDynamicPropertyName与列统计把第二个重复列重命名为Name1、Name2的做法var headerNameCounts new Dictionarystring, int(); var config new CsvConfiguration(CultureInfo.InvariantCulture) { GetDynamicPropertyName args { var header args.Context.Reader?.HeaderRecord?[args.FieldIndex] ?? string.Empty; // ...先经 PrepareHeaderForMatch 处理... var name headerNameCounts[header] 1 ? ${header}{args.FieldIndex} : header; return name; }, };GetDynamicPropertyNameArgs提供FieldIndex当前列索引与Context读取上下文足以支撑以上各种按列位置定制的命名逻辑。六、适用场景与注意事项小结适合使用GetRecordsdynamic()的场景CSV 列结构不确定、或同一批数据存在多种表头变体只想快速预览/探查文件内容不想为每张表定义类需要把表头与值以键值对形式统一处理因为FastDynamicObject同时实现IDictionarystring, object?可强制转换为字典遍历。需要注意的边界属性值全部是字符串数字、日期等需要自行转换属性名来源于表头若表头不规范含空格、重复、为空应按上文方式配置命名委托GetRecordsdynamic()是惰性迭代务必在using块内完成求值避免ObjectDisposedException需要类型转换、默认值、索引映射等强类型能力时应改用 get-class-records 或 mapping-by-name 等方案。七、延伸阅读原文档Get Dynamic Records同类示例读取强类型记录 get-class-records、读取匿名类型记录 get-anonymous-type-records核心实现DynamicRecordCreator.cs、RecordCreatorFactory.cs、FastDynamicObject.cs命名配置CsvConfiguration.cs、ConfigurationFunctions.cs、GetDynamicPropertyName.cs测试验证DynamicTests.cs赞分享后端数据工程【免费下载链接】CsvHelperLibrary to help reading and writing CSV files项目地址https://gitcode.com/gh_mirrors/cs/CsvHelper点击查看免费下载相关推荐Wand-Enhancer 本地补丁实测不花一分钱激活 WeMod 专业版的完整上手指南Wand Enhancer 本地补丁实测不花一分钱激活 WeMod 专业版的完整上手指南 WeMod 专业版必须年费订阅吗不一定。Wand Enhancer后端数据工程CsvHelper 使用 GetRecordsT 将 CSV 行转换为类对象完整实战指南CsvHelper 使用 GetRecordsT 将 CSV 行转换为类对象完整实战指南 导读 本文围绕 CsvHelper 的 GetRecordsT后端数据工程CsvHelper 写入动态对象使用 ExpandoObject 与 IDynamicMetaObjectProvider 输出 CSVCsvHelper 写入动态对象使用 ExpandoObject 与 IDynamicMetaObjectProvider 输出 CSV 本指南聚焦 CsvH后端数据工程上一篇绝区零自动化脚本终极指南从下班只想躺到全自动一条龙的完整上手攻略下一篇网盘直链下载也能这么简单一个免费脚本八大网盘真实链接直接拿创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考