CSV をクラスにバインドする カラム名自由編 (C#)
CSV をクラスにバインドする (C#)は有用だが、CSV のヘッダには日本語やカッコなどの識別子に使えないカラム名を使いたいということがあるのでそれに対応する.
例えば
RowId,契約ID,開始日,終了日,顧客ID
0,C0001,2020-03-01T00:00:00.0000000,2020-04-01T00:00:00.0000000,0
1,C0002,2020-03-01T00:00:00.0000000,,1
みたいな CSV があるとする.
カラム名を指定するための HeaderName 属性を用意します.
[AttributeUsage(AttributeTargets.Property)]publicclassHeaderNameAttribute:Attribute{publicstringName{get;privateset;}publicHeaderNameAttribute(stringname){Name=name;}}
そしてバインド対象のクラスのプロパティーにHeaderName 属性を使ってカラム名をアノテーションします.
publicclassContract{publicintRowId{get;set;}[HeaderName("契約ID")]publicstringId{get;set;}[HeaderName("開始日")]publicDateTimeStartDate{get;set;}[HeaderName("終了日")]publicDateTime?StopDate{get;set;}[HeaderName("顧客ID")]publicintCustomerId{get;set;}}
後は Load を以下に変更すると、var contracts = Load<Contract>(fstream).ToArray();
でバインドできます.
publicstaticIEnumerable<T>Load<T>(Streamstream,Encodingencoding=null)whereT:class,new(){if(encoding==null)encoding=Encoding.UTF8;using(varreader=newStreamReader(stream,encoding)){varheader=reader.ReadLine().Split(",");varheaderNameMap=typeof(T).GetProperties().Select(e=>{vara=Attribute.GetCustomAttributes(e,typeof(HeaderNameAttribute));if(a.Length==0){returnnewKeyValuePair<string,string>(e.Name,e.Name);}else{returnnewKeyValuePair<string,string>((a[0]asHeaderNameAttribute).Name,e.Name);}}).ToDictionary(e=>e.Key,e=>e.Value);varheaderMap=newDictionary<string,int>();for(vari=0;i<header.Length;i++){headerMap[headerNameMap[header[i]]]=i;}while(true){varline=reader.ReadLine();if(line==null)break;yieldreturnBind<T>(headerMap,line.Split(","));}}}