CSVHelper - 将带有变量 header 的 CSV 映射到 object
CSVHelper - Mapping CSV with variable header to object
我正在尝试将一些 variable-format tab-separated 文件映射到我的 class,Foo:
public class Foo
{
public string A { get; set; }
public int? B { get; set; }
public double? C { get; set; }
}
文件 1:
A B C
string 1 1.0
文件 2:
Bee Sea
1 1.0
当 header 格式不变时,使用 CsvClassMap 使用 CsvHelper 很容易做到这一点:
public sealed class FooMap : CsvClassMap<Foo>
{
public FooMap()
{
Map(x => x.A).Name("A");
Map(x => x.B).Name("B");
Map(x => x.C).Name("C");
}
}
但是,当 header 格式可变时,这会变得复杂。
- 文件的宽度和高度可能不同。
- Header顺序不固定
- A 属性 每个文件最多只定义一次。
- 每个 属性 可能与各种 header 名称相关。
- Header 名称专用于单个 属性.
- 并非所有属性都必须在文件中定义。
映射到此 object 的最佳方式是什么?
我想我会为 headers 填充映射 table 到 headers 列上具有唯一键的属性,然后查找每个 header各自的属性.
目前调查方向:
public sealed class FooMap : CsvClassMap<Foo>
{
public FooMap()
{
}
public void SetHeaders(List<string> headers)
{
var dictionary = new Dictionary<string, List<string>>();
dictionary.Add("A", new List<string>() { "A", "Aay" });
dictionary.Add("B", new List<string>() { "B", "Bee" });
dictionary.Add("C", new List<string>() { "C", "Sea" });
...
}
}
您可以为每个 属性 指定多个名称,并使其在缺少字段时不会抛出异常。
这是一个使用 LINQPad 的例子:
void Main()
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvReader(reader))
{
writer.WriteLine("A Bee C");
writer.WriteLine("string 1 1.0");
writer.Flush();
stream.Position = 0;
csv.Configuration.Delimiter = " ";
csv.Configuration.WillThrowOnMissingField = false;
csv.Configuration.RegisterClassMap<FooMap>();
csv.GetRecords<Foo>().ToList().Dump();
}
}
public class Foo
{
public string A { get; set; }
public int? B { get; set; }
public double? C { get; set; }
public Guid? D { get; set; }
}
public class FooMap : CsvClassMap<Foo>
{
public FooMap()
{
Map( m => m.A ).Name( "A", "Aay" );
Map( m => m.B ).Name( "B", "Bee" );
Map( m => m.C ).Name( "C", "Sea" );
Map( m => m.D ).Name( "D", "Dee" );
}
}
结果:
A: string
B: 1
C: 1
D: null
我正在尝试将一些 variable-format tab-separated 文件映射到我的 class,Foo:
public class Foo
{
public string A { get; set; }
public int? B { get; set; }
public double? C { get; set; }
}
文件 1:
A B C
string 1 1.0
文件 2:
Bee Sea
1 1.0
当 header 格式不变时,使用 CsvClassMap 使用 CsvHelper 很容易做到这一点:
public sealed class FooMap : CsvClassMap<Foo>
{
public FooMap()
{
Map(x => x.A).Name("A");
Map(x => x.B).Name("B");
Map(x => x.C).Name("C");
}
}
但是,当 header 格式可变时,这会变得复杂。
- 文件的宽度和高度可能不同。
- Header顺序不固定
- A 属性 每个文件最多只定义一次。
- 每个 属性 可能与各种 header 名称相关。
- Header 名称专用于单个 属性.
- 并非所有属性都必须在文件中定义。
映射到此 object 的最佳方式是什么?
我想我会为 headers 填充映射 table 到 headers 列上具有唯一键的属性,然后查找每个 header各自的属性.
目前调查方向:
public sealed class FooMap : CsvClassMap<Foo>
{
public FooMap()
{
}
public void SetHeaders(List<string> headers)
{
var dictionary = new Dictionary<string, List<string>>();
dictionary.Add("A", new List<string>() { "A", "Aay" });
dictionary.Add("B", new List<string>() { "B", "Bee" });
dictionary.Add("C", new List<string>() { "C", "Sea" });
...
}
}
您可以为每个 属性 指定多个名称,并使其在缺少字段时不会抛出异常。
这是一个使用 LINQPad 的例子:
void Main()
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvReader(reader))
{
writer.WriteLine("A Bee C");
writer.WriteLine("string 1 1.0");
writer.Flush();
stream.Position = 0;
csv.Configuration.Delimiter = " ";
csv.Configuration.WillThrowOnMissingField = false;
csv.Configuration.RegisterClassMap<FooMap>();
csv.GetRecords<Foo>().ToList().Dump();
}
}
public class Foo
{
public string A { get; set; }
public int? B { get; set; }
public double? C { get; set; }
public Guid? D { get; set; }
}
public class FooMap : CsvClassMap<Foo>
{
public FooMap()
{
Map( m => m.A ).Name( "A", "Aay" );
Map( m => m.B ).Name( "B", "Bee" );
Map( m => m.C ).Name( "C", "Sea" );
Map( m => m.D ).Name( "D", "Dee" );
}
}
结果:
A: string
B: 1
C: 1
D: null