CsvHelperのConfigurationの設定や例外処理って、難しいと思っています。
なかなか資料が無いですし、たまに見かけても英語ばかりですし・・・。
今日は「CsvHelper.MissingFieldException」について紹介します。
現場レベルのCsvHelperの実装サンプルを作りました。
CSVデータの取得から、入力チェック、モデル変換まで網羅しています。
5つのシステムで実証したコピペで使い回せる実装法
» 【CsvHelper マスター講座】を見る
CsvHelper MissingFieldExceptionとは?
どんな例外?
不足しているフィールドが見つかったときにCsvHelper.MissingFieldExceptionがスローされます。
ConfigurationのMissingFieldFoundを使うと例外をスローする代わりに、実行する独自の関数を設定できます。
ポイントDetectColumnCountChanges = trueを設定した場合、CsvHelper.BadDataExceptionがスローされます。CsvHelper.MissingFieldExceptionはスローされません。
CsvHelper.BadDataExceptionについて知りたい方は「CsvHelper BadDataExceptionとは?」の記事で書いているので、そちらを見てください。
ソースコード例①
検証したCsvHelperは、バージョン 15.0.0です。
テストデータ
3行目のデータの列数が少なくなっています。
1,伊藤,東京都
2,佐藤,千葉県
3,
ソース
public class Test
{
public void Main()
{
try
{
var csvFormats = CsvHelperPerformer.GetRecords<CsvModel>(@"C:csvtest.csv");
}
catch (CsvHelper.MissingFieldException ex)
{
Console.WriteLine($"エラー:{ex.ReadingContext.Row}行目のデータの列数が不足しています。");
Console.WriteLine($"値:{ex.ReadingContext.RawRecord}");
}
}
}
public class CsvHelperPerformer
{
/// <summary>
/// ファイルを読み込む
/// </summary>
/// <typeparam name="T">変換するクラス</typeparam>
/// <param name="path">ファイル</param>
/// <returns></returns>
public static List<T> GetRecords<T>(string path)
{
using (var reader = new StreamReader(path, Encoding.GetEncoding("SHIFT_JIS")))
using (var csv = new CsvReader(reader, new CultureInfo("ja-JP", false)))
{
csv.Configuration.HasHeaderRecord = false;
//csv.Configuration.DetectColumnCountChanges = true; // trueを設定するとCsvHelper.BadDataExceptionがスローされます。
return csv.GetRecords<T>().ToList();
}
}
}
public class CsvModel
{
[Index(0)]
public int Id { get; set; }
[Index(1)]
public string Name { get; set; }
[Index(2)]
public string Address { get; set; }
}
// 【実行結果】
// エラー:3行目のデータの列数が不足しています。
// 値:3,
このようにデータの列数の不足を見つけたら、例外を発生させます。
列数が増えている場合には例外が発生しないので注意してください。
ソースコード例②
テストデータ
今度はヘッダーをつけて、4・5行目の2行のデータの列数が少なくなっています。
ID,NAME,ADDRESS
1,伊藤,東京都
2,佐藤,千葉県
3,
4,後藤
ソース
public class Test
{
public void Main()
{
var csvFormats = CsvHelperPerformer.GetRecords<CsvModel>(@"C:csvtest.csv");
}
}
public class CsvHelperPerformer
{
/// <summary>
/// ファイルを読み込む
/// </summary>
/// <typeparam name="T">変換するクラス</typeparam>
/// <param name="path">ファイル</param>
public static List GetRecords(string path)
{
using (var reader = new StreamReader(path, Encoding.GetEncoding("SHIFT_JIS")))
using (var csv = new CsvReader(reader, new CultureInfo("ja-JP", false)))
{
csv.Configuration.HasHeaderRecord = true; // ヘッダを追加したのでtrue
csv.Configuration.MissingFieldFound = (headerNames, index, context) =>
{
Console.WriteLine($"{context.Row}行目のデータの列数が不足しています。");
Console.WriteLine($"値:{context.RawRecord}");
//Console.WriteLine($"Field with names ['{string.Join("', '", headerNames)}'] at index '{index}' was not found.");
// ↑ headerNamesがどうやってもnullでした。
};
return csv.GetRecords<T>().ToList();
}
}
}
public class CsvModel
{
[Name("ID")] //←ヘッダの名称でマッピング
public int Id { get; set; }
[Name("NAME")]
public string Name { get; set; }
[Name("ADDRESS")]
public string Address { get; set; }
}
// 【実行結果】
// 4行目のデータの列数が不足しています。
// 値:3,
// 5行目のデータの列数が不足しています。
// 値:4,後藤
MissingFieldFoundを使うと、例外をスローする代わりに実行する独自の関数を設定できます。
例外をスローするとそこで処理が終了してしまいますが、このようにすれば最後までデータを読み込んでくれます。全データを読み込んでエラーメッセージを出したい時などに使えます。
まとめ
CsvHelperのConfigurationの設定や例外処理って、難しいですよね。
他のConfigurationの設定や例外について知りたい方は「CsvHelper Configurationの設定」の記事でも書いていますのでどうぞ。