Hatena::ブログ(Diary)

newta(にゅーた)の日記 このページをアンテナに追加 RSSフィード

2008-10-23

[][]S2Csv 0.0.2をリリース

S2Csv 0.0.2をリリースしました。

とりあえず、なんとなく動きそうな感じまでできたかなぁって思ったので

今度はseasar-user MLとかにもお知らせしました。

seasarHPの最新ニュースにも書いときました。


相変わらずフィードバックがあれば幸いです。

むしろ、ダメダメな感じなのでコードレビューしてください(><

もっとコードリーディングしないとだめだとつくづく感じた

プロダクト開発だった。。

これからもどんどんメンテしていこうかと思います。


あと、表記がs2csvだったりS2CSVだったりS2Csvだったりしてるけど

これからはS2Csvに統一して行こうと思います。

地味にこっそり直して行きます。。

2008-10-17

[][]こっそりS2Csvをリリースした

sandboxといいつつ、かなりの製品のレベルのやつが混じってる中

本当にsandboxの名にふさわしい、

砂遊びレベルなプロダクトですよ。


0.0.0.0.0.0.1とかにしたいとか書いてたバージョンから

ほとんど変わって無いので、

結構酷いバグがあるかも。


でも、1回区切りとしてアップしないと

違う意味でなあなあでリリースのびのびとかになりそうなので、

上げちゃいました。


フィードバックがあれば幸いです。


これからは、ちょこちょこ気に入らないところを直したり、

もうちょっとテストコードを増やしてテストせねばと思ってます。

2008-10-10

[][]DumpFileReaderカッコよかったです。

id:taediumさんごめんなさい。

s2csv作るに当たって、

以前の日記でパクろう参考にしようと

S2JDBC-Genのソースを見て適当CSV処理だーとか言ってたんですが

今日見てみたら、勘違いでした。。

たぶんWriterの方だけ見たんだと思います。

見てみたら、カッコよかったです。

メソッド名とか作りとか参考にします。


あと、ソースを見てたら違和感があったのでちょっと動かしてみたら

CSVファイルの読み込み処理がダメなとこを見つけた。


まず、データに改行を入れたとき、

""で括られてて、改行は残るのですが、

なぜかquoteの""まで登録されてしまう。

出力されたものに手で変更加えるとダメなのかな?


もう1つ、CSVファイルフォーマットで

quoteが閉じられないままファイルが終了すると、

なんだが次のファイルを見に行ってるみたい。

deptのデータファイルに仕込んだら、

empテーブルにはほげほげが無いよーとかエラー出てた。

なんかすごく危なそう。


出力されたcsvファイルはいじらない方がよいのかな?



Dontengで生成される0.9.1のやつだけど

SVNソースを見る限り更新されて無いようなので。

最新でもおきるかなと、、。





ただ簡単にハハ〜ンって直されちゃうと思うので、

そうするとs2csvの実装のしょぼさが良くわかるかと。。


やっぱりニッチだなぁ。s2csv。。

2008-10-06

[][]S2CSVを作ってみた

前回エントリ書いてから1ヶ月も経つんですね。

単なるユーティリティライブラリを作るのに時間掛かりすぎですね。

しかも完成してないし。

とりあえずs2csv-0.0.1です。(s2と付くけどSeasar未公認です)

本当は0.0.0.0.0.0.1くらいにしたい気持ちですがうざったいと思うのでやめました。

でもTODOもいっぱい残ってます。


自分の力の無さを感じます。。

勉強しないとダメですね。もっといいコードに触れないとダメですね。

勉強会に参加してみたいです。


まあ、ひどいもんなんですが公開しないのは悲しいので、

恥を忍んで公開します。

ソース入りjar

s2csv_DownLoad


csvのパース部分はSuperCSVをラップして使ってますので

DLしてください。

SuperCSV_DownLoad

作ってるときに使ってるのはver1.50です。

依存するのがいやなのと

気に入らない部分もあるのでSuperCSVは使わないようにするつもりです。

(本気になったら2,3日で変えれる気もするんだけど。。)


diconファイルに以下を書き加えます

■app.dicon

<include path="s2csv_supercsv.dicon"/>

■creator.dicon

<component class="org.seasar.s2csv.creator.CsvCreator"/>

■customizer.dicon

<component name="csvCustomizer" class="org.seasar.framework.container.customizer.CustomizerChain"/>


SAStrutsとかでDoltengのscaffoldしたDeptをベースに追記していきます。

SAStrutsのDoltengのやり方は過去エントリ参照。


あと、メッセージファイルとリソースパスに追加します。

とりあえずデフォルト

csv_application_ja.properties

英語は出来ないので作ってないです。


次にルートパッケージ.csvを作ってその下にcsvエンティティのクラスを作ります。

@CSVEntity(header=true)
public class DeptCsv {

	
	@CSVColumn(columnIndex=0)
	public Long id;

	@CSVColumn(columnIndex=1)
	public Integer deptNo;

	@CSVColumn(columnIndex=2)
	public String deptName;

	@CSVColumn(columnIndex=3)
	public String loc;

	@CSVColumn(columnIndex=4)
	public Integer versionNo;
}

いろんなバリデーションアノテーションはめんどいので今度。

まあCSVって打ってコード補完すれば、大体出ます。

CSVRequiredとか。strutsと同じ感じで、頭にCSVが付くだけ。

アノテーションでCSVConvertorについて設定位置が

CSVColumnのサブパラメータなんですが、

これは辞めようと思ってます。



Serviceにはこんな感じでcsvコントローラのファクトリをDIされるようにして

public class DeptService extends AbstractService<Dept> {

	public S2CSVCtrlFactory csvCtrlFactory;
	
	...

では書き出しから。

今回DBのデータを書き出す処理を書きます。

Writer writer = new FileWriter("C:\\csv_exsample_out2.csv");
S2CSVUtil.s2jdbcToCsv(DeptCsv.class, select(), writer);

こんな感じ。

select()で全部取得のやつを指定してますが、

where条件をつけたやつでももちろんOK。

handleで処理してるので、大量データでも大丈夫のはずです。

まあ、Utilのクラスはおまけみたいなものなのであんまり期待しないで下さい。


で、自分で処理するならこうです。

Serviceに書きます。

こっちが割と正しい書き出しです。

DBからデータを取ってくるとは限らないですしね。

S2CSVWriteCtrl<DeptCsv> csv_writer = 
	csvCtrlFactory.getWriteController(DeptCsv.class,writer);

List<Dept> all = super.findAll();

for(Dept o : all){
	
	DeptCsv deptCsv = Beans.createAndCopy(DeptCsv.class, o).execute();

	csv_writer.write(deptCsv);
}

csv_writer.close();

ちゃんとクローズしてね。


次、読み込み。

読み込みはちょっと長いです。

まずはやる気の無いUtilを使ったやつ。

Reader reader = new FileReader("C:\\csv_exsample_out.csv");
S2CSVUtil.csvToS2Jdbc(DeptCsv.class,Dept.class,reader);

updateはしませんinsertのみです。

validateチェックもしません。

コンバートエラーになったらExceptionになります。

やる気無いですね。


次、もうちょっとちゃんとして書くやつ


Reader reader = new FileReader("C:\\csv_exsample_out.csv");

S2CSVParseCtrl<DeptCsv> csv_parser = 
	 csvCtrlFactory.getParseController(DeptCsv.class, reader);
 
//parse時のバリデーションチェックをオフにして
//自分でvalidateメソッドを呼ぶ
csv_parser.setValidateFlag(false);

while(csv_parser.readNext()){
	 CSVValidateResult validateResult = csv_parser.validate();
	 
	 if(validateResult != null){
		//バリデーションエラー行
		 System.out.println(validateResult.getLineNo());
		 for(CSVMsg msg: validateResult.getMsgs()){
			 //バリデーションエラーメッセージ
			 System.out.println(msg);
		 }
		 continue;
	 }
	 
	 DeptCsv deptCsv = csv_parser.parse();
	 
	 Dept dept = Beans.createAndCopy(Dept.class,deptCsv).execute();
	 
	 insert(dept);
}

長くなりました。

でも、これで色々出来ると思います。


上の処理の形を変えたもの。

バリデーションエラーをExceptionで受け取る形

Reader reader = new FileReader("C:\\csv_exsample_out.csv");

S2CSVParseCtrl<DeptCsv> csv_parser = 
	 csvCtrlFactory.getParseController(DeptCsv.class, reader);
 
while(csv_parser.readNext()){
	 
	 try{
		 DeptCsv deptCsv = csv_parser.parse();
		 Dept dept = Beans.createAndCopy(Dept.class,deptCsv).execute();
		 insert(dept);
		 
	 }catch(CSVValidationResultRuntimeException e){
		 CSVValidateResult validateResult = e.getValidateResult();

			//バリデーションエラー行
			 System.out.println(validateResult.getLineNo());
			 for(CSVMsg msg: validateResult.getMsgs()){
				 //バリデーションエラーメッセージ
				 System.out.println(msg);
			 }
	 }
}

csv_parser.setValidateFlag(true);(初期値)

だと、parse()メソッドでバリデーションします。

バリデーションエラーがあると、CSVValidationResultRuntimeExceptionが投げられます。

値的にはvalidate()を自分で呼ぶのと一緒。

最後にUtilでバリデーションもするやつ


Reader reader = new FileReader("C:\\csv_exsample_out.csv");

List<CSVValidateResult> result =
	S2CSVUtil.csvValidateToS2Jdbc(DeptCsv.class,Dept.class,reader);

for(CSVValidateResult validateResult :result){

	//バリデーションエラー行
	 System.out.println(validateResult.getLineNo());
	 for(CSVMsg msg: validateResult.getMsgs()){
		 //バリデーションエラーメッセージ
		 System.out.println(msg);
	 }
}

全部流れて、

バリデーションエラーがあって

insertしなかったその結果を返す。

バリデーションエラーが無かったものはinsertされます。

あんまり汎用的ではないですね。


ちなみにCSVファイルフォーマット自体に問題があった場合

分かった段階でCSVFormatExceptionが投げられます。

1行づつ読んでるので、その行にならないと分かりません。

もし全部チェックしてからパース処理したい場合、

1回全部読んでから、再度Readerを作って

処理しないとダメです。


その辺は微妙とは思ってるので、

大量データも考えながら、何か考えたいですね。

キャッシュをフラグで取っておけるようにするとか。



とりあえず、ダメソースに対するコメントが一番欲しいです。。


あとs2csv作っててバリデーションについて

http://d.hatena.ne.jp/miztaka/20081004/p1

と同じようなことを思った。

アノテーションで手軽に設定できるバリデータが欲しい。

2008-09-12

[][]S2Csv

いまだ業務で使われるCSV。

最近の話だとS2JDBC-Genでも使われてるしw

これを手軽に使いたい。


前のプロジェクトの案件でCSVを扱いました。

CSVの変換とバリデーションを汎用的にするクラスを実装したのですが、

だるいですね。


それでS2を使ってちょっと前からほげほげしようと思ったわけですよ。

そして、前からほげほげしてたわけですよ。

で、今日、ふと思いついてs2csvで検索したらこれが出てきた↓

http://d.hatena.ne.jp/agt/searchdiary?word=s2csv


あれー?もうあるよー

セリフも「相変わらずCSVを扱う案件が多いんです。」

と似てるコンセプトw

でもリリースはされてないみたい。

まあ、やりたいことがちょっと違うし、

リリースされてないので、自分S2CSVは引き続き作ってみたいと思ってます。


でも、ちょっと参考にしようと思って

ファイルダウンロードしてみようと思ったけどダウンロードできない。残念。


とりあえず、僕の案はこんな感じ。

まずCSVエンティティを作る。

エンティティには条件をアノテーションで設定。


public class TestObj {

	public static final String hoMsg = "AAAA";
	
	@CSVRequired
	@CSVColumn(columnIndex=0)
	public String hoge;
	
	
	@CSVColumn(columnIndex=1)
	@CSVMaxLength(maxlength=10,args={"hoge2","10"})
	public String hoge2;

	@CSVColumn(columnIndex=2,quout=false,columnName="ほげINT")
	public Integer hogeInt;
	

	@CSVColumn(columnIndex=4)
	@CSVValidator(method="check", msgKey="errors.detail",args={"ほげほげエラー"})
	public String custamHoge;
	

	@CSVDateType
//	@CSVColumn(columnIndex=3,columnName="ほげ日付",convertor=@CSVConvertor(convertor=CSVDateConvertor.class,convertSetProp="pattern=yyyy/MM/dd"))
	@CSVColumn(columnIndex=3,columnName="ほげ日付",convToObjMethod="hogeObj",convToCSVMethod="hogeCnvString")
	public Date hogeDate;
	
	@CSVColumn(columnIndex=5, columnName="あいうえお")
	@CSVRequired//(args=hoMsg)
	public String req;

	@CSVColumn(columnIndex=6, convertor=@CSVConvertor(convertor=CSVDateConvertor.class,convertSetProp="pattern=yyyyMMdd"))
	public Date hogeDate2;
	
	public Date hogeObj(String columnData){
		
		return new Date();
	}
	
	public String hogeCnvString(Date d){
		
		return "2222/02/02";
	}
	
	public boolean check(String columndata){
		
		if(columndata == null){
			//return true;
			//バリデーションエラーテスト
			throw new CSVValidationException();
		}
		
		return true;
	}
}

こんなふうにCSVエンティティに定義をバシバシ書けば、




StringReader sr = new StringReader(csvData);
		
CSVParseController c = new CSVParseController(TestObj.class, sr);
		
List<TestBO> list = c.parse();

で、オブジェクトにパースしたり



StringReader sr = new StringReader(csvData);
		
CSVParseController c = new CSVParseController(TestObj.class, sr);
		
CSVValidateResult validateResult = c.validate();

で、バリデーションしたり。


エンティティ書くだけで、

読み込みも書き出しも

バリデーションも(←ここ重要)

3行ですよ。

まあ、最終的にparseとvalidateメソッド分けるか分からないけど。

(parseメソッドの中で標準でvalidateするようにするかも)


まあ、バリデーションを主軸に置きたいんですけどね。

だってCSVってユーザから入力されるケースも結構あるでしょ?

Webで画面から入力されるのと同様

バリデーションが当然必要なケースが多いと思います。

よくあるCSVライブラリってオブジェクトには変換してくれるけど、

チェックして、メッセージをStrutsっぽく設定できたり、

うまくハンドリングできそうなライブラリってなさげなので。。

知らないだけかもしれないけど。

バリデーションメッセージは文字列としてメッセージが出来上がった形式で

ListでvalidateResultに入ってます。

処理で失敗したカラムデータも入ってます。

行番号とかも。

とりあえず、エラーメッセージで使いそうなものは入れておく予定。

バリデーションエラーがあった時点で処理できる

handlerも設定できる予定。




で、とりあえず、動くものってのは作ったんだけど、

設計がめちゃめちゃすぎて人に見せれない。。。


でも、これならCSVエンティティを書くだけで、

バリデーションもできるし。

変換もできるようになるんで便利かと。

まあSAStrutsのActionFormみたいなもんですよね。

アノテーションはSAStrutsのを流用するのも考えたけど、

SAStrutsに依存するのば微妙すぎるのでやめましたw


でも、名前はCSVが頭に付くだけで、

機能的にも同等のチェックを行うのでわかりやすいかと。

とりあえずCSVって打って、補完リスト出せば関連するものが出てくるので

その辺も分かりやすいかと。


基本はCSVのカラムに関連するフィールドに

CSVColumnアノテーションを付けて、columnIndexを指定するだけ。

エラーメッセージにカラム名を追記したい場合

columnNameにカラム名を書いとく。

ここはヘッダ出力設定がされている場合、

ヘッダに使われます。

指定されて無い場合フィールド名がそのまま出ます。



コンバート処理はCSVColumnアノテーションの

convToCSVMethodと

convToObjMethodにメソッド名を書くと

エンティティから書き出し時、読み込み時に

それぞれエンティティのメソッドが呼ばれるようになってます。

もしくはCSVColumnConvertorを実装したクラスを

convertorに書けば、そのコンバータが呼ばれます。


CSVValidatorアノテーションを使えば

独自のバリデーションメソッドも呼び出せます。

(SAStruts見たいな感じで?)


CSVパース部分は手抜きでASL2.0ライブラリSuperCSV使ってます。

commons-csv使ってないのはなんとなくです。

たぶん、どっちにも対応できるけど。

でも中のソース見たらListをパースするやつが、

全部中でデータを持ってるので、大量データ対応するためと

別のライブラリ追加したくないので、

パーサとライターはとりあえずデフォルトのものは作るつもりです。

まあ業務によってダブルクォーテーションの処理とか、

改行処理とか違ったりすると思うので、

あくまでデフォルト。

なるたけ設定で対応できるようにはしたいけど。


でも作るのめんどいw

S2JDBC-GenでCSVを使ってると聞いて、

よしパクろうとw

喜び勇んでソースを覗いたのですが、

うわーん、適当CSV処理だーw

まあ、自分で吐いて、自分で飲むCSVだから

こんなもんだよね。。残念。



あと、SuperCSVとかはListのほかにMapやらBeanやらに書き出す機能がありますが、

S2CSVではMapとか単体のオブジェクト変換は考えてません。

1オブジェクトデータなんでCSVの性質上ない気がするし

(普通一覧データでしょ?)

Mapにすると設定を書くとこがなくなるので。


とりあえず、めちゃくちゃ設計なので

設計をもうちょっとちゃんとして修正して、

大量データ対応(ハンドラ周り)して、

S2が付くのに今はS2の便利Utilを使ってるだけなので、

せっかくなんでどっかでDIポイントを作って、

よさげな感じで実装したい。




でも、これは1番作ってみたいやつじゃないんですよね。。

1番完成させてみたいやつは

自分のスキル不足もあり、全く進まないと言う。。。




でも、成果物を出すのは重要だよね。

とりあえず、完成するまでこれやるのがいいのかなぁ。


使いたい人いるかな?

コメントつけてもらうと元気が出ますw


でも、S2と付けときながらSeasarプロダクトに承認されなかったら

(あまりにも業務よりで小粒なのでw)

名前を変えて出展かなw



あ、あと

これよりも便利そうなCSVライブラリがあるなら教えといて下さい。

出来上がったとき悲しくなるのでw