gpt4 book ai didi

c# - 将 CSV header 与 map 类进行比较

转载 作者:行者123 更新时间:2023-12-04 03:35:40 25 4
gpt4 key购买 nike

我有一个过程,我们编写了一个类,使用 CsvHelper ( https://joshclose.github.io/CsvHelper ) 将一个大 (ish) CSV 导入我们的应用程序。
我想将标题与 Map 进行比较以确保标题的完整性。我们从第 3 方获得 CSV 文件,我想确保它不会随着时间的推移而改变,我认为最好的方法是将其与 map 进行比较。
我们有一个这样设置的类(修剪):

public class VisitExport
{
public int? Count { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
}
及其对应的 map (也修剪过):
public class VisitMap : ClassMap<VisitExport>
{
public VisitMap()
{
Map(m => m.Count).Name("Count");
Map(m => m.CustomerName).Name("Customer Name");
Map(m => m.CustomerAddress).Name("Customer Address");
}
}
这是我用于读取 CSV 文件的代码,效果很好。我有一个针对错误的 try catch ,但理想情况下,如果它专门因标题未命中匹配而失败,我想专门处理它。
private void fileLoadedLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
try
{
var filePath = string.Empty;
data = new List<VisitExport>();

using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = new KnownFolder(KnownFolderType.Downloads).Path;
openFileDialog.Filter = "csv files (*.csv)|*.csv";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;

if (openFileDialog.ShowDialog() == DialogResult.OK)
{
filePath = openFileDialog.FileName;

var fileStream = openFileDialog.OpenFile();
var culture = CultureInfo.GetCultureInfo("en-GB");

using (StreamReader reader = new StreamReader(fileStream))
using (var readCsv = new CsvReader(reader, culture))
{
var map = new VisitMap();
readCsv.Context.RegisterClassMap(map);
var fileContent = readCsv.GetRecords<VisitExport>();
data = fileContent.ToList();
fileLoadedLink.Text = filePath;
viewModel.IsFileLoaded = true;
}
}
}
}
catch (CsvHelperException ex)
{
Console.WriteLine(ex.InnerException != null ? ex.InnerException.Message : ex.Message);
fileLoadedLink.Text = "Error loading file.";
viewModel.IsFileLoaded = false;
}
}
有没有办法比较 Csv 标题与我的 map ?

最佳答案

带有标题的 CSV 文件有两种基本情况:缺少 CSV 列和额外的 CSV 列。第一个已经被 CsvHelper 检测到而第二个的检测不是开箱即用的,需要对 CsvReader 进行子类化.
(由于 CsvHelper 按名称将 CSV 列映射到模型属性,因此在 CSV 文件中排列列的顺序不会被视为重大更改。)
请注意,这仅适用于实际包含标题的 CSV 文件。由于您没有设置 CsvConfiguration.HasHeaderRecord = false我假设这适用于您的用例。
以下是有关这两种情况中每一种的详细信息。
缺少 CSV 列。
目前,在这种情况下,默认情况下 CsvHelper 已经抛出异常。当找到未映射的数据模型属性时, CsvConfiguration.HeaderValidated 被调用。默认设置为 ConfigurationFunctions.HeaderValidated 其当前行为是抛出 HeaderValidationException 如果有任何未映射的模型属性。您可以替换或扩展 HeaderValidated如果您愿意,可以使用自己的逻辑:

var culture = CultureInfo.GetCultureInfo("en-GB");
var config = new CsvConfiguration (culture)
{
HeaderValidated = (args) =>
{
// Add additional logic as required here
ConfigurationFunctions.HeaderValidated(args);
},
};

using (var readCsv = new CsvReader(reader, config))
{
// Remainder unchanged
演示 fiddle #1 here .
额外的 CSV 列。
当前 CsvHelper发生这种情况时不会通知应用程序。见 Throw if csv contains unexpected columns #1032这证实了这不是开箱即用的。
GitHub comment , 用户 leopignataro建议一种解决方法,即子类化 CsvReader 并自行添加必要的验证逻辑。但是,评论中显示的版本似乎无法处理重复的列名或嵌入的引用。 CsvHelper的以下子类应该正确地做到这一点。它基于 CsvReader.ValidateHeader(ClassMap map, List<InvalidHeader> invalidHeaders) 中的逻辑.它递归地遍历传入的 ClassMap , 尝试找到对应于每个成员或构造函数参数的 CSV header ,并标记每个映射的索引。之后,如果有任何未映射的 header ,则提供 Action<CsvContext, List<string>> OnUnmappedCsvHeaders被调用以通知应用程序问题并在需要时抛出一些异常:
public class ValidatingCsvReader : CsvReader
{
public ValidatingCsvReader(TextReader reader, CultureInfo culture, bool leaveOpen = false) : this(new CsvParser(reader, culture, leaveOpen)) { }
public ValidatingCsvReader(TextReader reader, CsvConfiguration configuration) : this(new CsvParser(reader, configuration)) { }
public ValidatingCsvReader(IParser parser) : base(parser) { }

public Action<CsvContext, List<string>> OnUnmappedCsvHeaders { get; set; }

public override void ValidateHeader(Type type)
{
base.ValidateHeader(type);

var headerRecord = HeaderRecord;
var mapped = new BitArray(headerRecord.Length);
var map = Context.Maps[type];
FlagMappedHeaders(map, mapped);
var unmappedHeaders = Enumerable.Range(0, headerRecord.Length).Where(i => !mapped[i]).Select(i => headerRecord[i]).ToList();
if (unmappedHeaders.Count > 0)
{
OnUnmappedCsvHeaders?.Invoke(Context, unmappedHeaders);
}
}

protected virtual void FlagMappedHeaders(ClassMap map, BitArray mapped)
{
// Logic adapted from https://github.com/JoshClose/CsvHelper/blob/0d753ff09294b425e4bc5ab346145702eeeb1b6f/src/CsvHelper/CsvReader.cs#L157
// By https://github.com/JoshClose
foreach (var parameter in map.ParameterMaps)
{
if (parameter.Data.Ignore)
continue;
if (parameter.Data.IsConstantSet)
// If ConvertUsing and Constant don't require a header.
continue;
if (parameter.Data.IsIndexSet && !parameter.Data.IsNameSet)
// If there is only an index set, we don't want to validate the header name.
continue;

if (parameter.ConstructorTypeMap != null)
{
FlagMappedHeaders(parameter.ConstructorTypeMap, mapped);
}
else if (parameter.ReferenceMap != null)
{
FlagMappedHeaders(parameter.ReferenceMap.Data.Mapping, mapped);
}
else
{
var index = GetFieldIndex(parameter.Data.Names.ToArray(), parameter.Data.NameIndex, true);
if (index >= 0)
mapped.Set(index, true);
}
}

foreach (var memberMap in map.MemberMaps)
{
if (memberMap.Data.Ignore || !CanRead(memberMap))
continue;
if (memberMap.Data.ReadingConvertExpression != null || memberMap.Data.IsConstantSet)
// If ConvertUsing and Constant don't require a header.
continue;
if (memberMap.Data.IsIndexSet && !memberMap.Data.IsNameSet)
// If there is only an index set, we don't want to validate the header name.
continue;

var index = GetFieldIndex(memberMap.Data.Names.ToArray(), memberMap.Data.NameIndex, true);
if (index >= 0)
mapped.Set(index, true);
}

foreach (var referenceMap in map.ReferenceMaps)
{
if (!CanRead(referenceMap))
continue;

FlagMappedHeaders(referenceMap.Data.Mapping, mapped);
}
}
}
然后在您的代码中,处理 OnUnmappedCsvHeaders随心所欲的回调,例如抛出 CsvHelperException或其他一些自定义异常:
using (var readCsv  = new ValidatingCsvReader(reader, culture)
{
OnUnmappedCsvHeaders = (context, headers) => throw new CsvHelperException(context, string.Format("Unmapped CSV headers: \"{0}\"", string.Join(",", headers))),
})
演示 fiddle :
  • #2 (your model) .
  • #3 (with external references) .
  • #4 (duplicate names) .
  • #5 (using the auto-generated map) .

  • 这可以使用额外的测试,例如用于具有参数化构造函数和附加可变属性的数据模型。

    关于c# - 将 CSV header 与 map 类进行比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66984134/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com