playframework scalaのリフレクション
先のブログのプロジェクトの機能追加として、CSVファイルから、部品表(PartsStructureテーブル)を登録する機能について検討。
CVSファイルを、play.api.mvc.MultipartFormData.FilePartで取り込む。
取り込んだファイルをjava.io.FileInputStream,scala.io.Source.fromInputStreamでscala.io.BufferedSourceに取り込み、getLineメソッドでIteratorへ。
val file = atach.ref.file val fis = new FileInputStream(file) val src = Source.fromInputStream(fis) val itr = src.getLines
Iterator[String]の各行と変換後のBean名をパラメータで受け取り、Any型を戻り値とする変換クラス(CastBean.scala)を製作。
大雑把な仕様は、
第2パラメータで受けたbeanクラス名(String)から、beanの属性を取得。
第1パラメータで受けたStringをカンマで区切り、順に取得した属性にセットする。
参考にしたページ
scala.reflect.runtime.{universe= ru}のruntimeMirrorを生成。
beanクラス名から、classSymbolを生成。
Class.forName(clsName).newInstance()にてインスタンス取得後、
runtimeMirror.reflectメソッドで、インスタンスミラー取得。
classSymbolのdeclarationメソッドを利用して各fieldSymbolを取得。
その際、field名が必要になる。
field名の取得は、Class.forName(clsName).getDeclaredFields()にて、フィールド名のコレクション
を取得し、各フィールドに対しruntimeMirror.newTermNameメソッドでフィールドシンボルを取得。
インスタンスミラーのreflectFieldメソッドにてフィールドミラー取得。
あとは、setterメソッドでCSVで切り分けた値をセット。
def stringToBean(it: String , clsName: String):Any = { import scala.reflect.runtime.{universe => ru} val runtimeMirror=ru.runtimeMirror(Thread.currentThread.getContextClassLoader) val beanClassSymbol = runtimeMirror.staticClass(clsName).toType val bcm = Class.forName(clsName).newInstance() val bim = runtimeMirror.reflect(bcm) var i:Int=0 if(it!="") { for(f<-Class.forName(clsName).getDeclaredFields()){ val fieldSymbol= beanClassSymbol.declaration(ru.newTermName(f.getName())).asTerm val fm =bim.reflectField(fieldSymbol) fm.set( f.getType().getName() match { case "long" => it.split(",").apply(i) match{ case "" => 0 case _ => it.split(",").apply(i).toInt } case _ => it.split(",").apply(i) match{ case "" => " " case _ => it.split(",").apply(i) } } ) i += 1 } } bcm }
呼び出し元では、Any型を、asInstanceメソッドで、テーブルの型へ変換する。
クラスネーム(String)をパラメータで受けることにより、PSテーブルとの依存度を下げれた。
他のテーブルへの出力にも利用可能。