最近のPlay-Java開発環境について

もうね、一年振りとかになっちゃいました。

最近またPlay触ってまして、またしてもいくつかハマったりしたので備忘録。
例によってPlay-Javaですが、一年経ってバージョンが2.4.2(Activator 1.3.5)になってます。
「Damiya」だそうです。

Scalaも良いのですが、Java8が出てきて、最近は結構Javaの方も良いかな、と思っています。

Play Framework 2.3 (Java)でのサブプロジェクトとリバースルーティング

Play!で本格的なアプリケーションを開発するのであれば、いくつかのサブプロジェクトに分割するのが自然かと思います。
ただ、現状サブプロジェクトの構成を整備するは結構敷居が高いと感じました。
設定ファイルやテンプレートがScalaDSLっぽい形になっているので、実行する段になってエラーが発生し、しかも原因が突き止め難い。
恐らく参照するドキュメントのバージョン違いによる差異もあるのかと思いますが、私も実際に動くようにするために、結構な試行錯誤を重ねました。
※粘って頑張っているうちはできなくて、諦めて一旦寝たらあっさり出来てしまったパターン <= これに名前を付けたい!

基本的にはこちらを参照しました。
結果的にこちらの内容にウソはなかった様ですが、テンプレート内部からのリバース・ルーティング周りがなかなか上手くいきませんでした。。

やっと一通り動くところまで来ましたので、現在の構成を記載しておきたいと思います。

前提

「foo」というベースプロジェクトに、「bar」というサブプロジェクトを追加する場合を考えます。

ポイントとなるファイル

  1. routes --- 言わずと知れた、URLとコントローラのマッピングを記載するファイルですね。「foo」と「bar」にそれぞれ用意します。
  2. build.sbt --- sbt用のビルドファイル。Playの(sbtの?)バージョンによってはbuild.scalaだったりするので、ググる時にちょっと混乱します。これも、「foo」と「bar」にそれぞれ用意します。
  3. コントローラ --- play.mvc.Controller を継承した、javaのソースファイル。ここにリクエストに対する処理を記述しますね。
  4. テンプレート --- 「**.scala.html」ってやつ。play new(2.3 では activator new ですが)したときの雛形で、外枠(main.scala.html)とコンテンツ(**.scala.html)に分かれているので、この形は踏襲します。

ディレクトリ構成

結局これが80%くらいですかね。
どういう構成が正解かを突き止めるのに結構苦労しました。

foo
- build.sbt
- app
-- controllers
--- FooController.java(雛形だと Application.java) 
-- views
--- index.scala.html 
--- main.scala.html 
- conf
-- application.conf 
-- routes
- modules
-- bar
--- build.sbt
--- app
---- controllers
----- bar
------ BarController.java
---- views
----- index.scala.html 
----- main.scala.html 
--- conf
---- bar.routes

サブプロジェクトを構成するのに関係するもののみ書いてあります。この構成に正確に従う必要があります。

build.sbt の内容

「foo」側のbuild.sbt(foo/build.sbt)
name := """foo"""

version := "1.0-SNAPSHOT"

lazy val bar = (project in file("modules/bar")).enablePlugins(PlayJava)
lazy val root = (project in file(".")).enablePlugins(PlayJava).dependsOn(bar).aggregate(bar)

scalaVersion := "2.11.1"

libraryDependencies ++= Seq(
  javaJdbc,
  javaEbean,
  cache,
  javaWs
)

「bar」というサブプロジェクトを生成して、「foo」に追加するイメージなんですね。

「bar」側のbuild.sbt(foo/modules/build.sbt)
name := "bar"

scalaVersion := "2.11.1"

libraryDependencies ++= Seq(
  javaJdbc,
  javaEbean,
  cache,
  javaWs
)

routes の内容

「foo」側のroutes(foo/conf/routes)
GET     /assets/*file               controllers.Assets.at(path="/public", file)
GET     /                           controllers.Application.index()

  • > /bar bar.Routes
「bar」側のroutes(foo/modules/bar/conf/bar.routes)
GET     /assets/*file               controllers.Assets.at(path="/public", file)

GET     /               controllers.bar.BarController.index()

※先程の 参考ページ には「Assets.java」についてもサブプロジェクト側に用意すべし、みたいなことが書いてありますがとりあえず上手くいかなかったので共通のものを利用する設定のままとなっています。

テンプレートの内容

この、リバースルーティングの記述の正解がなかなか突き止められなかったんですよねー
一応、上記の構成を前提として以下のような書き方で動作するはずです。

foo/modules/bar/app/views/main.scala.html
@(title: String)(content: Html)

<!DOCTYPE html>

<html>
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript"></script>
    </head>
    <body>
        @content
    </body>
</html>

まあ、要するにこちらに関してはそのままですが、このファイルをちゃんと用意するのが大事なようです。

foo/modules/bar/app/views/index.scala.html
@(message: String)

@main("This is Bar's index") {
	<p>@message</p>
	<p>
	<a href="@controllers.bar.routes.BarController.another()">another page</a>
	</p>
}

ここの「another page」のリバースルーティングの部分が鬼門でしたね。
「BarController」のロケーション(パッケージ)から言うと「@routes.controllers.bar.BarController」と書きたくなるところですが、どうやら違ったようです。

終わりに

Play Frameworkもいい加減枯れてきたかな、と思っていたのですが、触ってみるとまだ色々成長中な感じですね。
サブプロジェクトの構成も(一応動きましたけど)結構まだこなれてない感じがするので、今後また改善(=仕様変更)されるのかも知れません。

Play Framework 2.3 を触る

インストール

ちょっと時間ができたので、思う所あってとりあえず最新のPlay Frameworkを触ってみました。
当然色々とハマりどころがあったので備忘録。

Play Frameworkは2014/05末に2.3がリリースされ、TypeSafe Activatorと統合されたことで結構大幅に変わった様子。
Activatorというのは本来は「リアクティブ・プログラミング」のためのプラットフォームのようですが、今回はまあそこは大きくは絡まないです。
但しTypeSafe、そう、Scalaを作ったMartin Odersky先生の会社が出して来ているものなので、今後Playを使うならとりあえず乗っかっとけって感じでしょうね。

暫く自前Mac環境の更新をしていなかったので、結果としていろいろとアップデート祭りになってしまいました。

  • JDK1.7のインストール(Playが対応していないのでScala 2.11が正式対応していないので、1.8はちょっと見送り。Oracle提供になって、インストールディレクトリが違うんですよね。ユーティリティの "Java Preference" も無くなってるし、「環境設定」の「Java」でもうまく切り替わらなかったので、とりあえず「/System/Library/Frameworks/JavaVM.framework/Versions」の「CurrentJDK」のリンクを「/Library/Java/JavaVirtualMachines/jdk1.7.0_67.jdk/Contents/」に張り替えることで対応。正しいやり方ではないかもね。)
  • MacPorts => Homebrewへの移行(PATHの設定を /usr/local/bin 及び /usr/local/sbin を見るように変更)
  • Scala 2.11のインストール(Homebrewを利用)
  • Play 2.3(というかActivator)のインストール

Play Framework のダウンロードサイトからダウンロードすると、普通にActivatorのzipが落ちてきます。(このあたり、まだホームページのドキュメントが追いついてないですね。)解凍してPATHを通します。

説明に従って「play new」の代わりに「activator new」とやると、どのテンプレートを使うか聞かれます。
今回はとりあえずplay-java(3)を選びました。

Browse the list of templates: http://typesafe.com/activator/templates
Choose from these featured templates or enter a template name:
1) minimal-java
2) minimal-scala
3) play-java
4) play-scala
(hit tab to see a list of all templates)

色々ダウンロードが始まります。
そして出来上がったフォルダに移動して「activator ui」とやると、しばらくすると勝手にブラウザで開発用ページが開きやがります。
僕はもともとPlayにそんなに慣れていたわけでもないので、とりあえずこのuiベースでいろいろやってみることにしました。


eclipseでプロジェクトを開く

上記のブラウザ上から、コードの編集/保存、コンパイル・実行まで可能になっていますが、やはりコーディングはEclipseからやりたいですよね。
もともとPlayにもあった機能ですが、Eclipse用のプロジェクトを作る機能もブラウザ上に搭載されています。
ちょっと分かり難いのですが、「Code」メニューの上の方のプルダウンメニュー(?)から、「Open Project in Eclipse」というのを選ぶことができます。

ここからEclipse用のプロジェクトファイルを生成することができます。
あとはEclipseで適当なワークスペースを開いて、Package Explorerコンテキストメニューから「Import」=>「General」=>「Existing Project into Workspace」といういつものやつです。

デモ動画に従ってみる => jQueryはどこ?

Play Frameworkのトップページには、非常にテンポの良い開発デモ動画があったので、とりあえずこれに従って試してみました。(デモではIntelliJ Ideaを使ってますね)
但し例によってこの動画はまだPlay 2.3には対応していないため、そっくりそのままというわけにはいきませんでした。
特に今回作ったプロジェクトのデフォルトテンプレートにはjQueryのインポート文が入っていなかったため、デモ動画で紹介されているAjaxの部分が動きません。
ここが一番ハマりました。

いろいろ調べた結果、Play 2.3からは、この辺りは「WebJars」なる仕組みを使ってインポートをするみたいですね。
ここ⇒Migration23 - 2.3.x を読むと書いてあるのですが、Playを触っていない人間にはなかなか良く分からんのですよ。
で、散々探しまわった挙句、こちらに親切に書いてくれていました。ありがとうございます。

Play Framework 2.3 For Java ことはじめ #7 bootstrap3を使う - まーぽんって誰がつけたの?

つまり、

  • build.sbt の「libraryDependencies」に、WebJarsとjqueryに関する記述を追加する。

libraryDependencies ++= Seq(
javaJdbc,
javaEbean,
cache,
javaWs,
"org.webjars" %% "webjars-play" % "2.3.0",
"org.webjars" % "jquery" % "2.1.1"
)

↑こんな感じ。但し、バージョンの指定の根拠は分からず。。

  • conf/routes にwebjarsの取得パスを記述

GET /webjars/*file controllers.WebJarAssets.at(file)

  • テンプレートに、次のように script タグを追加。

これでようやく、デモ動画の内容が一通り実現できました。ふー。

MacOSで astah* のプラグインを作ってみる。

各種UMLエディタの中で、個人的には一番使いやすいと思っているastah システム設計、ソフトウェア開発支援ツール | astah*プラグインの開発を志しておりまして、
とりあえずチュートリアルを触ってみようかと。

Mac OS環境ということと、Eclipseのバージョン周りでちょっと調整が必要だったので書いておきます。

その前に、既存のプラグインである コード生成プラグイン | Astah を試してみたのですが、MacOS環境だからなのか、ちょっと上手く動作しませんでした。具体的には、コードの生成中に固まってしまい、コードの生成がまともにできませんでした。うーん。

そこでまあ、結局自前でプラグインを作ってみる方が早いかな、というのが経緯。
まずはこちらからSDKをダウンロード。
こちらのチュートリアルに従い、まずは環境を整備します。

私はEclipse派なので、Eclipseで環境を作ろうとしたのですが、Eclipseの場合はM2Eclipse | M2Eclipseが必要になります、ということらしい。
ところが、このm2eがEclipseの現時点での最新バージョン「luna」向けに調整されているとのことで、
既にインストール済であった「juno」にはどうもうまくインストールできない。
止むを得ず「luna」を導入して、m2eをインストールします。

(逆に Scala IDE for Eclipse はまだ「luna」に対応していなかったりする。。)

とにかくまあ、「luna」であれば一応環境準備はできました。
で、ワークスペースを開いて、「astah-generate-project」コマンドで生成したプロジェクトを「Import」⇒「Maven」⇒「Existing Maven Projects」からインポートすると、
何か「pom.xml」周りにエラーが出ています。どうやら java build path に指定のある astah-api.jar が読み込めないらしい。

どうもM2_REPO周りの設定がうまくいっていないらしいというのは分かったのですが、 java build path の設定から「Configure Variables」をいじろうとしても、何か「M2_REPO (non modifiable)」とかなっていて変更できないらしい。うーん。

これ散々ハマったのですが、こちら に解決方法がありました。

これもちょっと分かり難いのですが、Eclipseの「環境設定(projectのPropertiesではできません)」⇒「Maven」⇒「User Settings」のUser Settingsの指定を、「(astah plugin SDK のインストールディレクトリ)/conf/settings.xml」としてやることで解決できました。

基本的にはm2eのバージョン差異なんですかね。ぐむー。

一応これで、ビルドエラーは綺麗に無くなりました。

キャプテン・ハーロックとガッチャマン

すみません、キャプテン・ハーロックを観て来ました

もはやこの世代の人間の責任感とも言うべき感情に駆られまして。
何となく思い立って、映画批評など書いてみようかと思いました。
たまたまちょっと前に「ガッチャマン」も観て来たので、その比較において語ってみたいと思います。

僕が観に行ったのは20:20からのレイトショーでしたが、
さすがに公開初日だけあってか、結構な数のおっさん(自分も含めて)・おばさんが入っていました。
ちなみに3D上映でしたが、これが大正解。

基本的にネタバレなしでいきます。
ただまあ、基本的にネタバレしても問題ない映画だと思います。

総評

いやー良かったです。良かったですよ、ハーロック
いろいろとツッコミどころはあるけれども、この映画に与えられた役割という意味では完璧すぎる出来だと思いました。原作ファンとしても十分満足できる出来でしたし、日本人にこんなものが作れるんだという事実に感動しました。
あちこちで言われていますが、ガッチャマンが酷かっただけに嬉しかったです。

制作費

キャプテン・ハーロックの総制作費は27億5000万円(!)と言われています。
凄いですね。マーケティングのことは良く分かりませんが、今このテーマでそこまで突っ込むのは凄いなーと感じます。
ちなみに、ちょっと前の実写版「SPACE BATTLESHIP YAMATO」の制作費が20億円で、興業収入が41億円だそうです。
かつての人気コンテンツとはいえヤマトよりかなりマイナーな感のあるハーロックに、そこまで制作費を突っ込む判断はかなり思い切ったものだと思います。ただ、それだけ価値のある内容に仕上がっている、と個人的には思います。
一方のガッチャマンは、総制作費80億円なんていう話が出回っていますが、これはマユツバですよね。実際のところは6〜8億円というところらしいです。ただ実際、ガッチャマンも「お金かかってる感」はあるのですが、それは残念ながら「無駄に金かかってる感」でしかなかったです。

設定・脚本

これは最近のトレンドなのか、ハーロックガッチャマンともに所謂「リブート」という手法を用いています。原作の設定やメカ、ディティール等を踏襲しつつ、物語としては一旦白紙の状態から組み立て直す、というものです。まだ観てないですが、新スーパーマン「マン・オブ・スティール」もそういう事みたいですね。
ガッチャマンはとにかく脚本が酷かった。台無しです。昔のアニメなので、設定が非現実的で無理や矛盾があるのは批判すること自体に意味が無いと思っています。ただガッチャマンの場合はそういうレベルの話ではなかったです。イラっとするセリフも随所にちりばめられていました。このあたりについてはネット上でも散々語られていますので、ここで細かく語る必要もないでしょう。原作の方がまだまともだったと言っていいと思います。
キャプテン・ハーロックについても色々設定上の無理があり、ストーリーも結構苦しいです。が、そこはさすがに福井晴敏さん、所々に唸らされる設定もあり、何とか纏めたという感じでしょうか。ただこの映画に関して言えば、脚本に関してはそれなりの形と奥行きを与えられていて、世界観を表現できれば良いのだと思います。「パシフィック・リム」と同じ理由において。

映像表現

これこそが、この手の映画(アニメの実写化)に最も求められているものだと思っています。
そしてこの点において、キャプテン・ハーロックは「見事」の一言に尽きます。今まで日本のCG技術はどうしてもハリウッドに敵わない感じがありましたが、この映画はハリウッド映画と初めて真正面から渡り合える出来だと感じました。
特筆すべきは、「生身の人間をCGで表現することに初めて成功している」ことです。
有名な所では、昔「ファイナル・ファンタジー」がこれをやろうとして大コケしていますね。
ピクサーやディズニーはこの辺りを良く分かっていて(?)、CG映画は全てデフォルメしたキャラクターで構成していますよね。学術的には「不気味の谷」と言われる感覚があって、リアルな映像に近づくと、逆に現実との微妙なズレが気になって違和感を感じます。あまり有名ではないですが、数年前にロバート・ゼメキス監督の「ベオウルフ/呪われし勇者」という映画がこの技術に挑戦しています。映像の完成度という意味ではかなり凄いとは思いましたが、正に映画全体をこの「不気味さ」が覆っていました。
格闘ゲーム等では既に美しい3Dキャラがいろいろ出て来ていますが、動きが限定されているのと、セリフを話したり、細かい表情を使い分けたりするケースはあまりなかったのではと思います。「龍が如く」ってやったことないんですけど、どうなんですかね。
キャプテン・ハーロックでは、遂にこの、実物の人間を表現することに成功したと言ってもいいのでは無いでしょうか。漫画・アニメに登場したキャラクターの処理も絶妙だったと思いますし、ヒロインの美しさにも素直に引き込まれました。また、アルカディア号の砲台やエンジンの表現、特に金属がすり減っている感じの表現が見事でした。3Dとの相性も抜群でしたね。
あと、CGで人間を表現するメリットとして、キャラクターの「子供時代」をそのまま表現できるという事実にも気付かされました。

メカデザイン・衣装デザイン

個人的には「ガッチャマン」のスーツは原作の設定を生かしつつ、なかなかカッコ良く纏めたな、と思っています。ただ、それ以外はかなりちぐはぐな印象を受けました。
一方、ハーロックは細部に渡って全ての設定に命が吹き込まれている印象です。例えば敵の司令官は物語の鍵となる事故の結果足が不自由になり、ハイテクな車椅子みたいなのに乗っているのですが、それすらもやたらとカッコ良かったです。あとは乗組員が着ている宇宙服とか、敵の戦艦「オケアノス」とかもシビれましたね。一部中世をモチーフにしたような都市のデザインも美しかったです。

原作へのレスペクト

アルカディア号はもちろんかっこ良かったですし、船内の雰囲気とか、ハーロックの肩に乗っている「トリさん」とか、ケイ/ミーメ/ヤッタラン、そして大山トチロー。
原作に登場する設定やキャラクター達が見事に再現されていました。
「SPACE BATTLESHIP YAMATO」では、敢えて生身のデスラーを登場させないことでうまく「逃げて」いましたが(それはそれで正解だったと思っています)、今回は全てを真正面からやり遂げていると言えると思います。恐らく原作ファンも文句はないのではないでしょうか。
(ただ一部、小栗旬三浦春馬といった声優陣には批判もあるみたいですが。。)

そしてそして、エンドロールが終わった後に浮かび上がった、松本先生直筆(?)のハーロックアルカディア号。
これを見せられたら原作ファンは黙るしかないでしょう。


というわけで、オタクキャラ全開で書いてしまいましたが、是非観に行って欲しいです、「キャプテン・ハーロック」。
ヴェネツィア国際映画祭でも好評だったようですし、海外からの評判が楽しみです。

Lift / Boot.scala内のMenuの書き方について

訳あって久々にがっつりLiftを触っているので、いくつか備忘録を作ろうと思います。
今さらLiftについて書いてもあまり世の中の役には立たないかもしれませんけどねー

Liftには色々なDSLが定義されていて、しかも同じ事ができるバリエーションが結構あるので、結構混乱します。
Web上のリソースもいろいろあるのですが、わりとどれも1つの書き方しか取り上げていないので、どの書き方が最適なのか考え出すとなかなか情報を揃えるのが大変だったりします。

(今使っている環境は、ちょっと古いかも知れませんが Scala 2.9 / Lift 2.4 です。)

先ずはBoot.scala内のMenuの書き方について。

DSLを使った簡潔な書き方。

Menu("menu1_name", "メニュー1タイトル") / "menu1_path" >> LocParam1

メニューの名前

ここで、

  • 「menu1_name」: このメニューの名前(内部的な処理に使う?)
  • 「メニュー1タイトル」:メニューリストに表示される、表示文字列

そしてこの「menu1_name」は省略することができて、

Menu("menu1_title") / "menu1_path" >> LocParam1

という書き方もできます。
この場合、先述の「menu1_name」は「Helpers.randomString(20)」によって、自動的にランダムな文字列が設定されるようです。

また、

Menu.i("menu1_title") / "menu1_path" >> LocParam1

という書き方で、「名前」と「表示文字列」の両方とも「"menu1_title"」になります。

(※備考)例えば「名前」からURLを取得したいとき、

LiftRules.siteMap.open_!.findLoc("menu1_name").open_!.calcDefaultHref

みたいに取得出来ます。しかしこれ回りくどいですね。。

メニューのパス

そして、「menu1_path」は2つの意味(役割)を兼ねています。

  1. ページのURL(ドキュメントルートからのパス)
  2. ページに使うテンプレートのパス(/ドキュメントルート/src/main/webapp からのパス)

尚、"menu1_path" は、Implicit Conversionによって String から net.liftweb.sitemap.LocPath に変換されます。
ここでちょっと戸惑うのが、例えば

Menu("menu1_name") / "path/to/menu1" >> LocParam1

みたいなことをやろうとすると、ページのURLが「path%2Fto%2Fmenu1」といったように、URLエンコードされてしまうことです。(テンプレートは読み込めるのですが)
こういった場合には、このように

Menu("menu1_name") / "path" / "to" / "menu1" >> LocParam1

LocPathを演算子 "/" で連結する形になります。
どうだいー?分かり難いだろー?

表示条件等

「LocParam1」は、このメニューを表示するにあたっての条件等が入ります。
これは複数指定することもでき、逆に省略することも出来て、省略した場合は常にメニューが表示されることになります。
ここには「net.liftweb.sitemap.Loc.LocParam」のインスタンスが指定されます。
例えば、既存の「Hidden」というobjectを指定すると、メニューリストには表示されないページを定義することができます。

ベーシックな書き方。

上記はいわゆるDSLを使った書き方で、Implicit ConversionやSyntax Sugarをガンガン使っています。
便利ではありますが書き方を憶えていないとScalaの知識だけでは何の事だかなかなか分かり難いですよね。
(そういった意味で、僕は個人的にはあまりDSLの乱用は好きではないです。)

但し、MenuをScalaの文法に従って普通に定義すると以下のような形になります。
これはこれで分かり難いw

Menu(Loc("menu1_name", Link(List("path", "of", "page"), true, "url"), "メニュー1タイトル", LocParam1)),

まず、Linkインスタンスの説明から。

Link(List("path", "of", "page"), true, "url")

この記述では、class net.liftweb.sitemap.Loc.Linkのコンパニオンオブジェクトであるobject Linkのapplyメソッドをファクトリーとして使用し、インスタンスを生成しています。それぞれの引数ですが、

  • 「List("path", "of", "page")」は例によってページのURLと、テンプレートのパスの2つの役割を担っています。
  • 「true」は、このパスを完全一致として扱うかどうかを示すBoolean値とされています。つまり、このケースは「/path/of/page/hoge」にはマッチしないということらしいです。(ただ、「false」を指定すると、マッチすることになりますが、Menuの中で使っている限りはNot Foundになります。なので用途が良く分かりません)
  • 3つ目の引数「"url"」は、このLinkオブジェクトをHTML上にレンダリングする時に出力されるurlのようです。但し、1つ目の引数(List)と異なるurlを指定すると別のページへのリンクが提示される訳で、基本的には一つ目の引数(List)と三つ目の引数(String)は同じ内容を違う形式で指定することになるようです。
そしてLocの生成

結局のところ、Menuは1つのLocインスタンスだけを引数に取って生成されます。

Loc("menu1_name", Link(List("path", "of", "page"), true, "url"), "メニュー1タイトル", LocParam1)

ここまでくると、DSLを使った記法との対応が見て取れますね。
結局のところ、要素としては

  • リンクの名前
  • 表示文字列
  • サイト上のパス及びテンプレートのパスを兼ねたパス情報
  • リンクを表示する条件(等)

の4つ。
そしてLink(実際にはLink.apply())の第二引数/第三引数は(少なくとも普通の使い方をする分には)大きな意味はなさそうです。

Conclusion

さて、いろいろ悩みましたが、ここはやはり流儀に従って素直にDSLを使って完結に書いた方が良さそうですね。
悩んで損した。

Scalaでjson-simpleを使う

久しぶりのブログですね。
この間に震災があったりとかいろいろあったわけで。
いろいろと思う所はある訳ですが、とにかく前に進もうと頑張っています。
非常に幸せなことに、周りのいろいろな人に良い影響を分けてもらっています。

で結局セコい技術ネタなんですけどね。

久々にScalaでいろいろやっていまして、JSONを触ろうと思いまして。
Liftでもライブラリは用意されているんですがね、やっぱjson-simpleの方が使い慣れてるしポータブルだろうと。

で、このように使ってみたところ

val jsonobj = new JSONObject
jsonobj.put("num",new Integer(100))

こんなエラーが出る訳ですね。

error: overloaded method value put with alternatives (K,V)V  *1

こういうエラーの出方がScalaの悩ましいところ。

で、結局これ何かというと、JSONObjectっていうのはHashMapを継承して実装されているのですが、そのHashMapにジェネリクスの型が指定されていないことが原因で起きているんですね。

で、json-simpleに限っては次のような回避方法がありました。

val jsonobj = new java.util.HashMap[String,Object]
jsonobj.put("num",new Integer(100))
JSONValue.toJSONString(jsonobj)

つまり、きっちり型が指定されたHashMapでデータを作ってから変換してあげれば大丈夫。


でもこのあたりScala利用時の一般論として、きっちりジェネリクス対応していないライブラリを使ってわけのわかんない怒られ方をした場合に、こういうパターンがあることを知っておくと無意味でストレスフルな時間を回避できるかもですね。

*1:K with K,_20)_20) forSome { type _20 >: V with V } cannot be applied to (java.lang.String,java.lang.Integer) obj.put("a",new Integer(1