「CsvHelperって、CsvClassMapを使わないとマッピングできないよね?」
「CsvHelperのマッピングって、もっと簡単にできないのかな?」
こんなことを悩んでいませんか?
こういった疑問に答えます。
今日は「CsvHelperでCsvClassMapを使わないマッピング方法」について紹介します。
現場レベルのCsvHelperの実装サンプルを作りました。
CSVデータの取得から、入力チェック、モデル変換まで網羅しています。
5つのシステムで実証したコピペで使い回せる実装法
» 【CsvHelper マスター講座】を見る
CsvHelper.Configuration.CsvClassMapとは?
どう使うの?
CsvHelper.Configuration.CsvClassMapを継承したマッピング用クラスを作成してマッピングをします。
※バージョン 15.0.0は、CsvHelper.Configuration.ClassMapです。クラス名が変わっています。
CsvClassMapを使う方法
検証したCsvHelperは、バージョン 15.0.0です。
テストデータ
伊藤,東京都
佐藤,千葉県
ソース
public class Test
{
public void Main()
{
var members = CsvHelperPerformer.GetRecords<Member, MemberMapper>(@"C:\csv\test.csv");
foreach (var member in members)
{
Console.WriteLine($"Name:{member.Name} Address:{member.Address}");
}
}
}
public class CsvHelperPerformer
{
/// <summary>
/// ファイルを読み込む
/// </summary>
/// <typeparam name="TModel">変換するクラス</typeparam>
/// <typeparam name="TMapper">ファイル</typeparam>
/// <param name="path"></param>
/// <returns></returns>
public static List<TModel> GetRecords<TModel, TMapper>(string path) where TMapper : CsvHelper.Configuration.ClassMap
{
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.RegisterClassMap<TMapper>();
return csv.GetRecords<TModel>().ToList();
}
}
}
/// <summary>
/// データ格納用クラス
/// </summary>
public class Member
{
public string Name { get; set; }
public string Address { get; set; }
}
/// <summary>
/// マッピング用クラス
/// </summary>
public class MemberMapper : CsvHelper.Configuration.ClassMap<Member> // ←データ格納用クラスを指定
{
public MemberMapper()
{
Map(x => x.Name).Index(0); // x.Xxxxには、Memberのメンバを設定。.Index(X)には、Csvのインデックスを設定
Map(x => x.Address).Index(1);
}
}
// 【実行結果】
// Name:伊藤 Address:東京都
// Name:佐藤 Address:千葉県
Genericsを使用して汎用的にしたソースです。
呼び出し側のCsvHelperPerformer.GetRecords<Member, MemberMapper>を変えることでクラスを汎用的に使えるようにしています。
ポイントをまとめておきます。
ポイント
- データ格納用のクラスとマッピング用クラスを作ります。
- マッピング用クラスは、CsvHelper.Configuration.ClassMap<Member>とデータ格納クラスを指定して継承します。
-
マッピングは、Map(x => x.Name).Index(0);で指定します。
- x.Xxxxには、データ格納用クラスのメンバを設定します。
- .Index(X)には、Csvのインデックスを設定します。
※この場合は、データ格納クラスのNameに、Csvのインデックス0のデータを格納するという意味になります。
マッピング用クラスを作るのも一つや二つならいいけど、何個も作るとなるとけっこう面倒です。
マッピングクラスを作らない方が、ずっと簡単です。
次に、CsvClassMap(=マッピング用クラス)を使わないマッピングの方法を紹介します。
CsvClassMapを使わない方法
テストデータ
先ほどのと同じデータです。
ソース
public class Test
{
public void Main()
{
var members = CsvHelperPerformer.GetRecords<Member>(@"C:\csv\test.csv");
foreach (var member in members)
{
Console.WriteLine($"Name:{member.Name} Address:{member.Address}");
}
}
}
public class CsvHelperPerformer
{
/// <summary>
/// ファイルを読み込む
/// </summary>
/// <typeparam name="TModel">変換するクラス</typeparam>
/// <param name="path"></param>
/// <returns></returns>
public static List<TModel> GetRecords<TModel>(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.RegisterClassMap<TMapper>(); //不要です。
return csv.GetRecords<TModel>().ToList();
}
}
}
/// <summary>
/// データ格納用クラス
/// </summary>
public class Member
{
[Index(0)] // ←マッピング用クラスのかわりにインデックスを指定する
public string Name { get; set; }
[Index(1)]
public string Address { get; set; }
}
// 【実行結果】
// Name:伊藤 Address:東京都
// Name:佐藤 Address:千葉県
マッピング用クラスがなくなって、csv.Configuration.RegisterClassMap<TMapper>(); の設定が必要なくなります。
GetRecordsメソッドの記述もシンプルになって、呼び出し側もパラメータが減ってスッキリさせることができました。
ポイントのおさらいです。
ポイント
- データ格納用のクラスだけを作ります。マッピング用クラスは不要です。
-
マッピングは、[Index(0)]で指定します。
- [Index(x)]には、Csvのインデックスを設定します。
※この場合は、データ格納クラスのNameに、Csvのインデックス0のデータを格納するという意味になります。
まとめ
CsvHelperでCsvClassMapを使わないマッピング方法について紹介しました。
マッピングで特定の値をnullとして扱う方法についても書いています。興味がある方は「マッピングで特定の値をnullとして扱う方法」の記事をどうぞ。
DateTime型(日付型)に変換してマッピングする方法について知りたい方は「CsvHelper DateTime型(日付型)に変換してマッピングする方法」をどうぞ。
なかなか情報が少ないですが、CsvHelperは使いこなせれば便利だと思います。
他にも知らない設定や使い方があるんじゃないかと思います。興味がある方は「CsvHelper Configurationの設定」の記事をどうぞ。
こちらの記事も読まれています!