CSVHelper でカンマ区切りの数値を読ませる

お仕事で使っていてはまったのでメモ。

お題としては、普通のCSVではなくてTSV(タブ区切り)形式のファイルを読み込むというもので、その中の項目のひとつに数値(3桁カンマ区切り)が含まれている、というもの。

で、サンプルを参考にしながら組んでいったわけですが、デフォルトのままだとマッピング時に例外が発生してしまいます。

型 'CsvHelper.TypeConversion.CsvTypeConverterException' のハンドルされていない例外が CsvHelper.dll で発生しました 追加情報:The conversion cannot be performed.

コンバーターが対応していないのならと、いそいそとCustomTypeConverterを書いていたのですが、本家のソースを眺めていると、もっと簡単に以下のオプションをマッピングの定義に追加指定するだけで良いことに気が付きました。

Map(m => m.column3).Index(2).TypeConverterOption(NumberStyles.Number);

ちなみに今回のケースだと、上記のオプションではなく DecimalConverter を明示的に指定することでも読み込むことができます。じゃあ、なぜそちらを使わなかったかというと、空値を読めなかったから。

お題には書き忘れてましたが、値として空白もありうるという難題でした・・。

パッケージにはNull許可のDecimal用のコンバータは用意されておらず、代わりに(?) NullableConverter というコンバータがあるのですが、これだとカンマ区切りの数値には対応していないという(^^;;

まぁ、コンバーターの詳細は この辺 を読んでもらうことにして、最後に確認用に書いたコードを貼り付けておきます。

class Program
{
  static void Main(string[] args)
  {
    var lines =
      "column1\tcolumn2\tcolumn3\r\n" +
      "hoge\t1000\t1,000\r\n";

    using (var sr = new StringReader(lines))
    {
      using (var cr = new CsvReader(sr))
      {
        cr.Configuration.Delimiter = "\t";
        cr.Configuration.RegisterClassMap<DataClassMap>();

        var records = cr.GetRecords<Data>();

        foreach (var record in records)
        {
          Console.WriteLine("{0},{1},{2}", record.column1, record.column2, record.column3);
        }
      }
    }
  }
}

class Data
{
  public string column1 { get; set; }
  public int column2 { get; set; }
  public decimal column3 { get; set; }
}

class DataClassMap : CsvClassMap<Data>
{
  public DataClassMap()
  {
    Map(m => m.column1).Index(0);
    Map(m => m.column2).Index(1);
    Map(m => m.column3).Index(2).TypeConverterOption(NumberStyles.Number);
  }
}