读取 CSV 行并映射到 class with collection of subclass
Read CSV row and map to class with collection of subclass
我正在读取 CSV 文件。
没有headers。
我需要将它映射到一个 class,它有一个 collection 的子 object。
我知道 collection.
中的 object 数量
Public Class Foo{
public int id {get; set;}
public Bar[] bars {get; set;}
public class Bar{
public int id {get; set;}
public string str {get; set;}
}
}
我正在尝试使用 CSVHelper 完成此操作
我尝试创建如下所示的映射器。
但是我只是收到以下错误:
CsvHelper.TypeConversion.TypeConverterException: '无法进行转换。
public sealed class Mapper : ClassMap<Foo>
{
public Mapper()
{
Map(m => m.id).Index(0);
Map(m => m.bars).Index(1, 2);
}
}
似乎带有 2 个参数的索引重载只希望转换 collection 个值,而不是 object 个由多列构造的值。
我的实际代码 collection 大小为 80,objects 上有 5 个字段,因此将它们放到基础 Foo object 上并不理想。
我知道我可以将 CSV 提取为字符串和由行和逗号分隔的字符串,然后手动遍历它们,但使用适当的 CSV 库似乎更干净,更不容易出现疏忽。
我看到还有添加带有地图的引用的选项
References<BarMap>(m => m.Bars);
public sealed class BarMap : ClassMap<Bar>
{
public BarMap()
{
Map(m => m.id).Index(0);
Map(m => m.str).Index(1);
}
}
但我看不出如何为它适当地设置索引。
该引用不允许指定索引。
我认为无法将文件自动映射到您的 class,但我已经使用 DTO class 获得了所需的结果。
考虑到数据是:
0,0,0,Lorem
1,0,1,Ipsum
2,1,0,Dolor
3,1,1,Sit
4,1,2,Amet
运行以下代码
public static void Main(string[] args)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
};
IEnumerable<FoobarDto> records = null;
using (var reader = new StreamReader("file.csv"))
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<FoobarDtoMap>();
records = csv.GetRecords<FoobarDto>().ToList();
}
var finalRecords = records.GroupBy(x => x.Id).Select(x => new Foo { Id = x.Key, Bars = x.Select(f => f.Bar).ToArray() });
}
public class FoobarDto
{
public int Id { get; set; }
public Foo.Bar Bar { get; set; }
}
public class Foo
{
public int Id { get; set; }
public Bar[] Bars { get; set; }
public class Bar
{
public int Id { get; set; }
public string Str { get; set; }
}
}
public sealed class FoobarDtoMap : ClassMap<FoobarDto>
{
public FoobarDtoMap()
{
Map(m => m.Id).Index(1);
Map(m => m.Bar.Id).Index(2);
Map(m => m.Bar.Str).Index(3);
}
}
它给你正确的结果。
请注意,CsvHelper
索引 0 处应该有一个唯一列,以正确解析 csv 文件的每一行。
您应该能够在映射中使用 Convert
来获取 bars
。我假定有两个 Bar
记录,但您可以更改 for
循环以说明不同数量的 Bar
.
void Main()
{
var input = "1,1,Dolor,2,Lorem\n2,3,Sit,4,Ipsum";
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
};
using (var reader = new StringReader(input))
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<Mapper>();
var records = csv.GetRecords<Foo>().ToList();
}
}
public sealed class Mapper : ClassMap<Foo>
{
public Mapper()
{
Map(m => m.id).Index(0);
Map(m => m.bars).Convert(args =>
{
var bars = new List<Bar>();
for (int i = 1; i < 4; i += 2)
{
var bar = new Bar
{
id = args.Row.GetField<int>(i),
str = args.Row.GetField<string>(i + 1)
};
bars.Add(bar);
}
return bars.ToArray();
});
}
}
public class Foo
{
public int id { get; set; }
public Bar[] bars { get; set; }
}
public class Bar
{
public int id { get; set; }
public string str { get; set; }
}
我正在读取 CSV 文件。 没有headers。 我需要将它映射到一个 class,它有一个 collection 的子 object。 我知道 collection.
中的 object 数量Public Class Foo{
public int id {get; set;}
public Bar[] bars {get; set;}
public class Bar{
public int id {get; set;}
public string str {get; set;}
}
}
我正在尝试使用 CSVHelper 完成此操作 我尝试创建如下所示的映射器。 但是我只是收到以下错误: CsvHelper.TypeConversion.TypeConverterException: '无法进行转换。
public sealed class Mapper : ClassMap<Foo>
{
public Mapper()
{
Map(m => m.id).Index(0);
Map(m => m.bars).Index(1, 2);
}
}
似乎带有 2 个参数的索引重载只希望转换 collection 个值,而不是 object 个由多列构造的值。
我的实际代码 collection 大小为 80,objects 上有 5 个字段,因此将它们放到基础 Foo object 上并不理想。
我知道我可以将 CSV 提取为字符串和由行和逗号分隔的字符串,然后手动遍历它们,但使用适当的 CSV 库似乎更干净,更不容易出现疏忽。
我看到还有添加带有地图的引用的选项
References<BarMap>(m => m.Bars);
public sealed class BarMap : ClassMap<Bar>
{
public BarMap()
{
Map(m => m.id).Index(0);
Map(m => m.str).Index(1);
}
}
但我看不出如何为它适当地设置索引。 该引用不允许指定索引。
我认为无法将文件自动映射到您的 class,但我已经使用 DTO class 获得了所需的结果。 考虑到数据是:
0,0,0,Lorem
1,0,1,Ipsum
2,1,0,Dolor
3,1,1,Sit
4,1,2,Amet
运行以下代码
public static void Main(string[] args)
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
};
IEnumerable<FoobarDto> records = null;
using (var reader = new StreamReader("file.csv"))
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<FoobarDtoMap>();
records = csv.GetRecords<FoobarDto>().ToList();
}
var finalRecords = records.GroupBy(x => x.Id).Select(x => new Foo { Id = x.Key, Bars = x.Select(f => f.Bar).ToArray() });
}
public class FoobarDto
{
public int Id { get; set; }
public Foo.Bar Bar { get; set; }
}
public class Foo
{
public int Id { get; set; }
public Bar[] Bars { get; set; }
public class Bar
{
public int Id { get; set; }
public string Str { get; set; }
}
}
public sealed class FoobarDtoMap : ClassMap<FoobarDto>
{
public FoobarDtoMap()
{
Map(m => m.Id).Index(1);
Map(m => m.Bar.Id).Index(2);
Map(m => m.Bar.Str).Index(3);
}
}
它给你正确的结果。
请注意,CsvHelper
索引 0 处应该有一个唯一列,以正确解析 csv 文件的每一行。
您应该能够在映射中使用 Convert
来获取 bars
。我假定有两个 Bar
记录,但您可以更改 for
循环以说明不同数量的 Bar
.
void Main()
{
var input = "1,1,Dolor,2,Lorem\n2,3,Sit,4,Ipsum";
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
};
using (var reader = new StringReader(input))
using (var csv = new CsvReader(reader, config))
{
csv.Context.RegisterClassMap<Mapper>();
var records = csv.GetRecords<Foo>().ToList();
}
}
public sealed class Mapper : ClassMap<Foo>
{
public Mapper()
{
Map(m => m.id).Index(0);
Map(m => m.bars).Convert(args =>
{
var bars = new List<Bar>();
for (int i = 1; i < 4; i += 2)
{
var bar = new Bar
{
id = args.Row.GetField<int>(i),
str = args.Row.GetField<string>(i + 1)
};
bars.Add(bar);
}
return bars.ToArray();
});
}
}
public class Foo
{
public int id { get; set; }
public Bar[] bars { get; set; }
}
public class Bar
{
public int id { get; set; }
public string str { get; set; }
}