LoginSignup
11
12

More than 5 years have passed since last update.

CSVHelperで読込時に正規表現チェック

Last updated at Posted at 2015-12-28

最終更新日:2015/12/28

前置き

CSVファイルをCSVHelperで読む時に型変換を行う場合、
Mapする際に [TypeConverter]メソッドをはさむ事で
メジャーな型やNullable、Enumなどに変換して取得する事が出来る。

ただ、郵便番号やメールアドレスのような形式が決まっている文字列も
型としては“String”であり、[TypeConverter] に「郵便番号型」なんてのは存在しない為(当たり前)、
こっち側でデータの中を読みつつ正しいかをチェックしてやる必要がある。
せっかくModelとMapを整えても、結局1項目ずつ読み取るのは何だか勿体無い。

目標

Mapする際に正規表現でパターンマッチングを行い、
パターンにマッチしないデータが存在した時は、他の型のコンバートエラー同様
[CsvTypeConverterException]を返してあげる。

対応方法

今回は「郵便番号(/\A[0-9]{3}-[0-9]{4}\z/)」の正規表現を例に
サンプルソースを書いてみます。

Custom Type Converter を作成する方法

RegexConverter.cs
public class RegexConverter : DefaultTypeConverter
{
  private Regex _regex;
  public RegexConverter(Regex reg) 
  {
    this._regex = reg;
  }

  public override object ConvertFromString(TypeConverterOptions options, string text)
  {
    if (text == null || !this._regex.IsMatch(text))
    {
      return base.ConvertFromString(options, text);
    }
    return text;
  }

  public override bool CanConvertFrom(Type type)
  {
    return type == typeof(string);
  }
}
}
ZipModelMap.cs
public class ZipModelMap : CsvClassMap<ZipModel>
{
  public ZipModelMap()
  {
    Regex zipReg = new Regex(@"\A[0-9]{3}-[0-9]{4}\z");
    Map(x => x.ZipCD).Name("ZipCD").TypeConverter(new RegexConverter(zipReg));
  }
}

ConvertUsing メソッドで無名関数を使用する方法

ZipModelMap.cs
public class ZipModelMap : CsvClassMap<ZipModel>
{
  public ZipModelMap()
  {
    Regex zipReg = new Regex(@"\A[0-9]{3}-[0-9]{4}\z");
    Map(x => x.ZipCD).ConvertUsing<string>(row =>
      {
        string val = row.GetField<string>("ZipCD");
        if (val == null || !zipReg.IsMatch(val))
        {
          thorw new CsvTypeConverterException("The conversion cannot be performed.");
        }
        return val;
      });
  }
}

まとめ

上記両パターン共に、指定した正規表現パターンに当てはまらないデータ存在時
[CsvTypeConverterException]を発生させてやることが出来た。
(タイプミス等のコンパイルエラーに関しては保障出来ないが・・・)

単なる文字列でも、指定した正規表現パターンにコンバート出来なかったら
型変換例外っていうのはちょっとズルいかな?

[Custom Type Converter]と[ConvertUsing]メソッドの使い分けとしては、
正規表現が連発するようなプロジェクトならクラス化しちゃって、
ちょこっと使いたい程度なら無名関数かな?っていう雰囲気で(
要するにただの決めです。どちらでも出来るのでお好きにどうぞ。

補足

[CsvTypeConverterException]は[CsvHelperException]を継承しており、
[CsvHelperException]はExceptionのData["CsvHelper"]に
エラーが発生した際の内容を格納している。

Row: '3' (1 based)
Type: 'CsvHelperSample.Model.ZipModel'
Field Index: '0' (0 based)
Field Name: 'ZipCD'
Field Value: '110001'

こいつと一緒にExceptionの内容を吐き出してやれば、
[GetRecodes<T>]メソッドで取得した時でも、どこでエラーが出たを簡単に取れる。

課題

[Configuration.IgnoreReadingExceptions]と掛け合わせれば
エラー全件取れるかは要考察。

参考サイト

CsvHelper
Custom TypeConverter · JoshClose/CsvHelper Wiki · GitHub

11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12