Hatena::ブログ(Diary)

すにぺっと

2012-01-27

Play 1.2.4でAnorm使って検索したときにRuntimeException: end of input expected

| 09:24 | Play 1.2.4でAnorm使って検索したときにRuntimeException: end of input expected - すにぺっと を含むブックマーク

普通にテーブルのデータを全件取得しているところで突然タイトルにあるエラーが出た。

Anorm内部ででているらしいことと上記メッセージ以外表示されずわけがわからなかったが、

原因はデータ不備によるものだったらしい。


対象テーブルのDDLはこんな感じ。

CREATE TABLE MyTable(
  id INT(10) NOT NULL AUTO_INCREMENT,
  name varchar(100) NOT NULL,
  age int(10),
  PRIMARY KEY (id)
);

Magicを使ってこんな感じで取得してた。

case class MyTable(
    id: Pk[Int],
    name: String,
    age:Int)
  object MyTable extends Magic[MyTable]

//これでエラー
MyTable.find().list()

調べた結果、あるレコードのageフィールドがnullになってたためにエラーだった。

もうちょっとわかりやすいメッセージがほしい・・・

テーブルにNOT NULLつけるか、ageプロパティをOption[Int]にすることで回避。

いつかまた同じエラーで悩みそうなんで覚えておこう。

2012-01-12

2つのリストをマージする

| 08:58 | 2つのリストをマージする - すにぺっと を含むブックマーク

2つのリストの要素同士を足して、新しいリストがほしい。

つまり、

case class A(num:Int)

というクラスがあり、これを要素とするListが2つある。

val list1 = List(A(1),A(2),A(3))
val list2 = List(A(10),A(20),A(30))

この2つのリストをマージして、

List[A] = List(A(11), A(22), A(33))

という結果を受け取りたい。

同じインデックス同士のAオブジェクトプロパティを足した結果のあたらしいAを生成して、それらをリストで返す。


twitterでつぶやいたところ、@kmizu氏より御神託が。

@syuta なるほど。標準ライブラリの範囲なら、(l1,l2).zipped.map(_.num+_.num).map(A(_)).toList ですかね。
携帯電話からのツイートなので、コンパイルエラーになったらすいません。 #scalajp
 in reply to @syuta

zippedを使ってmapで演算させる。

zippedってのは知らなかった。tupleのメソッドらしい。

def zipped [Repr1, El1, Repr2, El2] (implicit w1: (T1) ⇒ TraversableLike[El1, Repr1], w2: (T2) ⇒ IterableLike[El2, Repr2]): Zipped[Repr1, El1, Repr2, El2]
Wraps a tuple in a Zipped, which supports 2-ary generalisations of map, flatMap, filter, etc. Note that there must be an implicit value to convert this tuple's types into a TraversableLike or IterableLike.

scala> val tuple = (List(1,2,3),List('a','b','c'))
tuple: (List[Int], List[Char]) = (List(1, 2, 3),List(a, b, c))

scala> tuple.zipped map { (x,y) => x + ":" + y }
res6: List[java.lang.String] = List(1:a, 2:b, 3:c)
See also
Zipped Note: will not terminate for infinite-sized collections.

というわけでこうする。

scala> (list1,list2).zipped.map(_.num+_.num).map(A(_)).toList 
res21: List[A] = List(A(11), A(22), A(33))

期待した結果を得られた。

Tuple3でもTuple4でも同じように使用可能。


Scala実践プログラミング―オープンソース徹底活用
小笠原 啓 尾崎 智仁 関 隆 水島 宏太 今井 敬吾
秀和システム
売り上げランキング: 161092


Scalaスケーラブルプログラミング第2版
Martin Odersky Lex Spoon Bill Venners
インプレスジャパン
売り上げランキング: 26607

2011-12-11

Play-scala + GsonでScalaのクラスをシリアライズ

| 09:48 | Play-scala + GsonでScalaのクラスをシリアライズ - すにぺっと を含むブックマーク

以前

http://d.hatena.ne.jp/sy-2010/20110603/1307107943

にてplay-scalaをJSON形式で返すサンプルを書いたが、シリアライズする場合にScalaのクラスが

あるとうまくいかない。

なので、例えば下記のようなAnormのモデルでフィールドにPkやOptionがあったりすると、

シリアライズしてもなにも表示されない。

  case class User(
    id: Pk[Int],
    name:String
    email: Option[String])
  object User extends Magic[User]



そんなときはGsonのカスタムシリアライザ機能を使って、任意の値を返すようにする。

下記はPkとOption型を変換するシリアライザ。

  import com.google.gson._

    /**
     * for Pk[Int]
     */
    class PkSerializer extends JsonSerializer[Pk[Int]] {
      def serialize(src: Pk[Int], typeOfSrc: Type, context: JsonSerializationContext): JsonElement = {
        new JsonPrimitive(src.apply);
      }
    }

    /**
     * for Option
     */
    class OptionSerializer extends JsonSerializer[Option[Any]] {
      def serialize(src: Option[Any], typeOfSrc: Type, context: JsonSerializationContext): JsonElement = {
        new JsonPrimitive(src.getOrElse("none").toString());
      }
    }

Pkはその値をそのまま返し、OptionはSomeであればその値、Noneなら"none"という文字列を返す。


作成したカスタムシリアライザを適用してシリアライズ。

  import play._
  import play.mvc._
  import play.db.anorm._
  import com.google.gson._

  val builder = new GsonBuilder()
  builder.registerTypeAdapter(classOf[play.db.anorm.Pk[Int]], new PkSerializer)
  builder.registerTypeAdapter(classOf[Option[Any]], new OptionSerializer)
  val gson = builder.create()
  Json(gson.toJson(<Userオブジェクトのインスタンス>))

これで任意の型のシリアライズが可能。



in English

2011-11-19

タプルのリストをそれぞれの要素のリストに分割する

| 09:42 | タプルのリストをそれぞれの要素のリストに分割する - すにぺっと を含むブックマーク

List[(Int,String)]をList[Int]とList[String]に分割したくて、いろいろとやり方をためしていたら、

@ogattatsu氏に「unzipでできるよ」と教えてもらったのでやってみた。


scala> val tList = List((1,"hello"),(2,"world"))
tList: List[(Int, java.lang.String)] = List((1,hello), (2,world))

scala> val res = tList.unzip
res: (List[Int], List[java.lang.String]) = (List(1, 2),List(hello, world))

scala> res._1
res1: List[Int] = List(1, 2)

scala> res._2
res2: List[java.lang.String] = List(hello, world)

unzipで返ってくるTupleの1番目はIntのリスト、2番目はStringのリスト。


Mapでつかってみると、keyのリストとvalueのリストに分割できる。

scala> val map = Map("key1" -> 1,"key2" -> 2)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map(key1 -> 1, key2 -> 2)

scala> val res = map.unzip
res: (scala.collection.immutable.Iterable[java.lang.String], scala.collection.immutable.Iterable[Int]) = (List(key1, key2),List(1, 2))

scala> res._1
res3: scala.collection.immutable.Iterable[java.lang.String] = List(key1, key2)

scala> res._2
res4: scala.collection.immutable.Iterable[Int] = List(1, 2)


Scalaスケーラブルプログラミング第2版
Martin Odersky Lex Spoon Bill Venners
インプレスジャパン
売り上げランキング: 78285

2011-10-27

play-scalaで出たエラーの対処法

| 13:26 | play-scalaで出たエラーの対処法 - すにぺっと を含むブックマーク

・Eclipse 3.7

・Play framework 1.2.3

scalaモジュール 0.9.1

 

の環境にて、playでプロジェクトを作成し、

play ec

でEclipseプロジェクト化、インポートしようとしたら下記エラー。

object Application is not a member of package views

これはplay起動時に生成されるクラスがパスに入っていないのでエラーが出ているらしい。

どうやら、Scala Templateで生成される関数がないからエラーのよう。

一度play runで起動し、tmp/generatedディレクトリをビルドパスにいれればOK。

これでもなおらなければリフレッシュ、Eclipse再起動で。


解決かと思われたが、次は下記のようなエラー。

The Scala compiler crashed while compiling your project. 
This is a bug in the Scala compiler, not the IDE. Check the Erorr Log for details

これはplay-scalaが使用するScalaのバージョンと、自分の環境にはいっていた

Scala-IDEのバージョンが違ったためらしい。(Scala-IDEは2.9用を使用)

Scala-IDEの2.9を削除し、2.8用のプラグインを改めてインストールしたらエラーは消えた。