我有一个不可变的类,我想写入和读取 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;
}
}
Run Code Online (Sandbox Code Playgroud)
我将此写入 CSV 文件没有问题,但是当我尝试从文件中读取它时,出现以下异常。
没有成员映射到类型“CsvTest.Program+ImmutableTest”
但是,我已经在下面的映射类中映射了此类的成员。
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));
}
}
Run Code Online (Sandbox Code Playgroud)
我还尝试使用以下配置将阅读器配置为使用构造函数来构建对象。
Configuration config = new Configuration
{
IgnoreBlankLines = true
};
config.RegisterClassMap<ImmutableTestMap>();
config.ShouldUseConstructorParameters = type => true;
config.GetConstructor = type => type.GetConstructors()
.MaxBy(constructor => constructor.GetParameters().Length)
.FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)
这一切似乎都没有奏效。我哪里错了?
完整的 MCVE .NET Framework 控制台示例
安装软件包
Install-Package CsvHelper
Install-Package morelinq
Run Code Online (Sandbox Code Playgroud)
示例控制台程序
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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
ParameterMaps所以这里的问题是类映射中没有。
解决这个问题的简单方法,如上面的示例,是编写类似的内容
public ImmutableTestMap()
{
AutoMap();
Map(immutableTest => immutableTest.Id)
.Index(0)
.Name(nameof(ImmutableTest.Id).ToUpper());
}
Run Code Online (Sandbox Code Playgroud)
但这会导致一个问题,CSV文件中的列标题是ID,Name并且生成的参数映射是id,name。它们彼此不相等,因此读取器会抛出错误,指出无法找到名为 的列ID。它还表示您可以将 header validated 设置为 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>();
Run Code Online (Sandbox Code Playgroud)
尝试解析空字段但无法将其转换为 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];
}
}
Run Code Online (Sandbox Code Playgroud)
然而,我想说这更像是一种困境,而不是解决办法,并且肯定会对其他答案持开放态度。
| 归档时间: |
|
| 查看次数: |
619 次 |
| 最近记录: |