こんにちは、トミセン(@tomisenblog)です。
C#でCSVの読み込みに便利なCsvHelperですが、
「特定の値をnullとしてマッピングできないの?」
「nullのマッピングって、ITypeConverterを継承して自分で書かないとダメなのかな?」
やり方が分からなくて情報を探すだけで疲れてしまいがちです。
また、簡単に出来そうなサンプルを探して使ってみても、上手くいかなくて思ったより時間がかかってしまいますよね。
今回はそんなお悩みを解決する
ずばり!「CsvHelper マッピングで特定の値をnullとして扱う方法」についてご紹介します。
それでは一緒に学んでいきましょう!
現場レベルのCsvHelperの実装サンプルを作りました。
CSVデータの取得から、入力チェック、モデル変換まで網羅しています。
5つのシステムで実証したコピペで使い回せる実装法
» 【CsvHelper マスター講座】を見る
マッピングで特定の値をnullとして扱う方法
ここからはCsvHelperで特定の値をnullとして扱うための具体的な方法について解説していきます。サンプルコードを使って解説していきますので、一緒に学習していきましょう。
2つの方法でマッピングできるんです!
実は、CsvHelperでCSVファイルのデータをnullとして扱うには、2つの方法を使ってできるんです!最初は、TypeConverterOptionでデータを読み込む方法について詳しく解説します。
TypeConverterOptionとは、マッピングの型変換時に使用されるオプションを指定するメソッドです。DateTime型を読み込む際にも使用されるなど、とても多くの機能をもっています。
DateTime型(日付型)に変換してマッピングする方法について知りたい方は「CsvHelper DateTime型(日付型)に変換してマッピングする方法」をどうぞ。
方法①:TypeConverterOptionを使う方法
TypeConverterOptionを使ってマッピングしてみよう!
検証したCsvHelperは、バージョン 15.0.0です。
テストデータ
鈴木,NULL
NULL,千葉県
ソースコード
public class Test
{
public void Main()
{
List<Member> members = null;
using (var reader = new StreamReader(@"C:csvtest.csv", Encoding.GetEncoding("SHIFT_JIS")))
using (var csv = new CsvHelper.CsvReader(reader, new CultureInfo("ja-JP", false)))
{
csv.Configuration.HasHeaderRecord = false;
csv.Configuration.RegisterClassMap<MemberMapper>();
members = csv.GetRecords<Member>().ToList();
}
// ログ出力
foreach (var member in members)
{
Console.WriteLine($"Name:{ValueOf(member.Name)} Address:{ValueOf(member.Address)}");
}
}
/// <summary>
/// nullなら文字列"<null>"を返す
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public string ValueOf(object obj)
{
return obj?.ToString() ?? "<null>";
}
}
/// <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).TypeConverterOption.NullValues("NULL");
Map(x => x.Address).Index(1);
}
}
// 【実行結果】
// Name:鈴木 Address:NULL
// Name:<null> Address:千葉県
ソースコードだけでは分かりにくい部分があるので、いくつかのポイントをコードを見ながら解説していきます!
今回は実行結果がどうなったか確認するためにログを出力しています。それを指示しているのが次の場所になります。
// nullなら"<null>"を表示
Console.WriteLine($"Name:{ValueOf(member.Name)} Address:{ValueOf(member.Address)}");
マッピングの結果がnullなら”<null>”と表示されるようにしました。
ログ出力時にValueOf(member.Name)のところでValueOf()メソッドを使って“<null>”を表示するようにしています。
// ログ出力時に文字列を変換するメソッド
public string ValueOf(object obj)
{
// objがnullじゃなければ、ToString()する, nullなら"<null>"を表示する
return obj?.ToString() ?? "<null>";
}
ValueOf()メソッドの内容です。ここでは、ログ出力時に文字列を変換しているところです。
objがnullじゃなければ、ToString()した値を返して、nullなら”<null>”文字列を返すようにしています。
ログ出力の準備出来たら、早速マッピングでnullとして扱う特定の値を指定していく部分に移ります。マッピング用クラスを使って、処理を作っていきます。
// "NULL"という文字ならnullとして扱う
Map(x => x.Name).Index(0).TypeConverterOption.NullValues("NULL");
// 何もしない
Map(x => x.Address).Index(1);
.TypeConverterOption.NullValues(“NULL”)で“NULL”という文字ならnullとして扱うことができます。
指定しているプロパティとされていないものでは、結果はどうなっているでしょうか。
次の実行結果をご覧ください。
// 【実行結果】
// Name:鈴木 Address:NULL
// Name:<null> Address:千葉県
.TypeConverterOption.NullValues(“NULL”)を設定しているNameプロパティは<null>に変換されてnullとして扱われていることがわかります。
設定されていないAddressプロパティは“NULL”という文字列が表示されて、ただの文字列としてそのまま表示されています。
以上で「”NULL”という文字ならnullとして扱う」ことが出来ました!
方法②:NullValuesAttributeを使う方法
NullValuesAttributeを使ってマッピングしてみよう!
もう一つの方法はNullValuesAttributeを使用して読み込む方法です。
NullValuesAttributeを使用するとデータ格納用クラスに属性を追加するだけなので、比較的簡単に読み込むことができます。マッピング用クラスを作る必要はありません。
こちらもコードの解説をしていきます。是非マスターしてみてくださいね!
テストデータ
先ほどのと同じデータです。
ソースコード
public class Test
{
public void Main()
{
List<Member> members = null;
using (var reader = new StreamReader(@"C:csvtest.csv", Encoding.GetEncoding("SHIFT_JIS")))
using (var csv = new CsvHelper.CsvReader(reader, new CultureInfo("ja-JP", false)))
{
csv.Configuration.HasHeaderRecord = false;
members = csv.GetRecords<Member>().ToList();
}
// ログ出力
foreach (var member in members)
{
Console.WriteLine($"Name:{ValueOf(member.Name)} Address:{ValueOf(member.Address)}");
}
}
/// <summary>
/// nullなら文字列"<null>"を返す
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public string ValueOf(object obj)
{
return obj?.ToString() ?? "<null>";
}
}
/// <summary>
/// データ格納用クラス
/// </summary>
public class Member
{
[Index(0)]
[NullValues("NULL")]
public string Name { get; set; }
[Index(1)]
public string Address { get; set; }
}
// 【実行結果】
// Name:鈴木 Address:NULL
// Name:<null> Address:千葉県
こちらも、いくつかのポイントをコードを見ながら解説していきます!
先ほどと同じところは説明を省きます。
早速マッピングでnullとして扱う特定の値を指定していく部分を見てみましょう。
こちらではマッピング用クラスがないのでデータ格納用クラスを使って、設定していきます。
// "NULL"という文字ならnullとして扱う
[NullValues("NULL")]
public string Name { get; set; }
// 何もしない
public string Address { get; set; }
[NullValues(“NULL”)]と属性を設定することで“NULL”という文字ならnullとして扱うことができます。
結果はどうなっているでしょうか。次の実行結果をご覧ください。
// 【実行結果】
// Name:鈴木 Address:NULL
// Name:<null> Address:千葉県
結果は先ほどと同じですね。
[NullValues(“NULL”)]を設定しているNameプロパティは<null>に変換されてnullとして扱われていることがわかります。
設定されていないAddressプロパティは“NULL”という文字列が表示されて、ただの文字列としてそのまま表示されています。
このやり方でも「”NULL”という文字ならnullとして扱う」ことが確認出来ました!
最後に、非常に便利なポイントを一つだけ!
nullとして扱う特定の値は複数指定できます。次を見てください。
// "NULL"・"null"という文字ならnullとして扱う
[NullValues("NULL", "null")]
public string Name { get; set; }
カンマで区切って複数指定することができます。.TypeConverterOption.NullValues(“NULL”, “null”)でも複数指定ができます。
CsvHelperの属性やAttributesは調べるとけっこういろいろなものがあります。機会を見つけて記事にしていきたいと思います。
CsvHelperのAttributesはGitHubにドキュメントがあるので、下のリンクから見ることができます。
JoshClose/CsvHelper.Configuration.Attributes · GitHub
まとめ
CsvHelperのマッピングで特定の値をnullとして扱う方法について紹介しました。
マッピングや型変換はCsvHelperでけっこう分かりにくい部分なのかなって感じてます。
Configuration設定や使い方をマスターするとCSVの読み込みがもっと楽になりますよ。
興味のある方は「CsvHelper Configurationの設定」の記事をどうぞ。