我有一个不可变的类,我想写入和读取CSV文件。问题是,尽管已映射对象并设置了允许其正常工作的配置,但在读取CSV时却出现了异常。
为此,我正在使用CsvHelper。不可变的类如下所示。
public class ImmutableTest
{
public Guid Id { get; }
public string Name { get; }
public ImmutableTest(string name) : this(Guid.NewGuid(), name)
{
}
public ImmutableTest(Guid id, string name)
{
Id = id;
Name = name;
}
}
将其写入CSV文件没有问题,但是当我尝试从文件中读取它时,出现以下异常。
没有为“ CsvTest.Program + ImmutableTest”类型映射成员
但是,我已经在下面的map类中映射了该类的成员。
public sealed class ImmutableTestMap : ClassMap<ImmutableTest>
{
public ImmutableTestMap()
{
Map(immutableTest => immutableTest.Id)
.Index(0)
.Name(nameof(ImmutableTest.Id).ToUpper());
Map(immutableTest => immutableTest.Name)
.Index(1)
.Name(nameof(ImmutableTest.Name));
}
}
我还尝试通过以下配置将读取器配置为使用构造函数来构建对象。
Configuration config = new Configuration
{
IgnoreBlankLines = true
};
config.RegisterClassMap<ImmutableTestMap>();
config.ShouldUseConstructorParameters = type => true;
config.GetConstructor = type => type.GetConstructors()
.MaxBy(constructor => constructor.GetParameters().Length)
.FirstOrDefault();
这似乎没有用。我要去哪里错了?
完整的MCVE .NET Framework控制台示例
安装软件包
Install-Package CsvHelper
Install-Package morelinq
示例控制台程序
using System;
using System.IO;
using CsvHelper;
using CsvHelper.Configuration;
using MoreLinq;
namespace CsvTest
{
class Program
{
static void Main()
{
Configuration config = new Configuration
{
IgnoreBlankLines = true
};
config.RegisterClassMap<ImmutableTestMap>();
config.ShouldUseConstructorParameters = type => true;
config.GetConstructor = type => type.GetConstructors()
.MaxBy(constructor => constructor.GetParameters().Length)
.FirstOrDefault();
const string filePath = "Test.csv";
using (FileStream file = new FileStream(filePath, FileMode.Create))
using (StreamWriter fileWriter = new StreamWriter(file))
using (CsvSerializer csvSerializer = new CsvSerializer(fileWriter, config))
using (CsvWriter csvWriter = new CsvWriter(csvSerializer))
{
csvWriter.WriteHeader<ImmutableTest>();
csvWriter.NextRecord();
csvWriter.WriteRecord(new ImmutableTest("Test 1"));
csvWriter.NextRecord();
csvWriter.WriteRecord(new ImmutableTest("Test 2"));
csvWriter.NextRecord();
}
using (FileStream file = new FileStream(filePath, FileMode.Open))
using (StreamReader fileReader = new StreamReader(file))
using (CsvReader csvReader = new CsvReader(fileReader, config))
{
foreach (ImmutableTest record in csvReader.GetRecords<ImmutableTest>())
{
Console.WriteLine(record.Id);
Console.WriteLine(record.Name);
Console.WriteLine();
}
}
}
public sealed class ImmutableTestMap : ClassMap<ImmutableTest>
{
public ImmutableTestMap()
{
Map(immutableTest => immutableTest.Id)
.Index(0)
.Name(nameof(ImmutableTest.Id).ToUpper());
Map(immutableTest => immutableTest.Name)
.Index(1)
.Name(nameof(ImmutableTest.Name));
}
}
public class ImmutableTest
{
public Guid Id { get; }
public string Name { get; }
public ImmutableTest(string name) : this(Guid.NewGuid(), name)
{
}
public ImmutableTest(Guid id, string name)
{
Id = id;
Name = name;
}
}
}
}
答案 0 :(得分:1)
如果类型是不可变的,它将使用构造函数映射。您的构造函数变量名称必须与标题名称匹配。您可以使用import vehicle;
class MainProgram
{
public static int main()
{
vehicle bicycle= new vehicle("BMX", 2, "4.5 Bar");
vehicle car = new vehicle("VW", 4, "1.9 Bar");
.
.
class vehicle
{
//Instance variables:
private String name;
private int tyrenumbers;
private String tyrestyle;
private boolean drive;
来做到这一点。
Configuration.PrepareHeaderForMatch
答案 1 :(得分:0)
所以这里的问题是类映射中没有ParameterMaps
。
使用上面的示例解决此问题的简单方法是编写类似
的内容public ImmutableTestMap()
{
AutoMap();
Map(immutableTest => immutableTest.Id)
.Index(0)
.Name(nameof(ImmutableTest.Id).ToUpper());
}
但这会导致一个问题,即CSV文件中的列标题为ID
,Name
,而生成的参数映射为id
,name
。它们彼此不相等,因此读者抛出错误,说找不到名称为ID
的列。它还说您可以将头信息验证为null。玩完这个之后,我最终完成了配置
Configuration config = new Configuration
{
IgnoreBlankLines = true,
ShouldUseConstructorParameters = type => true,
GetConstructor = type => type.GetConstructors()
.MaxBy(constructor => constructor.GetParameters().Length)
.FirstOrDefault(),
HeaderValidated = null,
MissingFieldFound = null
};
config.RegisterClassMap<ImmutableTestMap>();
正在尝试解析空字段,并且无法将其转换为GUID。所以看起来这条路已经死了。
为解决此问题,我查看了检查自动映射生成的每个参数映射的“ id”值,然后将其替换为“ ID”。但是,这也不起作用,因为自动生成的地图是使用空名称生成的。仅在在配置中注册地图时调用SetMapDefaults时才分配名称。
所以我盲目地将参数映射的名称设置为显式定义的成员映射。
public ImmutableTestMap()
{
AutoMap();
Map(immutableTest => immutableTest.Id)
.Index(0)
.Name(nameof(ImmutableTest.Id).ToUpper());
Map(immutableTest => immutableTest.Name)
.Index(0)
.Name(nameof(ImmutableTest.Id));
if (MemberMaps.Count != ParameterMaps.Count)
{
return;
}
for (int i = 0; i < MemberMaps.Count; i++)
{
ParameterMaps[i].Data.Name = MemberMaps[i].Data.Names[0];
}
}
但是,我想说的是,这不只是一个解决之道,而且还可以解决其他问题。