JAX-RSとKnockout.jsでJSON連携

JavaEE Advent Calendar 2012のブログ記事で書いたJAX-RSのサンプルではjersyのViewableクラスを使用したJSPによる画面描画を紹介しました。今回はjsonデータを返却してクライアントサイドで描画する方法を紹介します。


今回はクライアントサイドの描画にシンプルで使い勝手の良いJavaScript MVVMフレームワークであるKnockout.jsを使用しました。

Knockout.jsのダウンロード

Knockout.jsのサイト(http://knockoutjs.com/)より最新版のKnockout.jsをダウンロードし(今回はknockout-2.2.1.jsを使用)プロジェクトのフォルダに配置します。

HTMLファイルの修正

HTMLをKnockout.jsのテンプレートに即して修正します。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
        <script type='text/javascript' src='knockout-2.2.1.js'></script>
    </head>
    <body>
        <h1>Hello World!</h1>
        <div id="list" data-bind="foreach: list">
            <div data-bind="text: $data"></div>
        </div>
        <script type="text/javascript">
            var viewModel = {
                list: ko.observableArray()
            };
            ko.applyBindings(viewModel);
            $(document).ready(function(){
                $.getJSON("webresources/hello/list", function(data){
                    viewModel.list(data);
                });
            });
        </script>
    </body>
</html>


knockout.jsのライブラリを読み込みviewModelの定義を行います。knockout.jsではJavaScript上のオブジェクトデータと画面上の表示をko.applyBindings()で紐づけることで双方向に同期を取る事ができます。JavaScriptの処理ではjQueryのgetJSONメソッドで取得したJSONデータをリストにセットしているだけですが、データのセットと同時に画面の再描画も行ってくれます。前回の記事ではlist.jspに一覧のテンプレート処理を記載していましたが<div id="list">タグに直接テンプレート処理を記載しています。

JAX-RSの修正

JAX-RSの処理をJSONを返却するように修正します。

package com.den2sn.mavenproject1;

import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;

@Path("hello")
public class HelloResource {

    @Context
    private UriInfo context;

    public HelloResource() {
    }

    @GET
    @Path("list")
    @Produces("application/json")
    public List<String> list() {
        List<String> messages = new ArrayList<String>();
        messages.add("message1");
        messages.add("message2");
        messages.add("message3");
        return messages;
    }
}


@Producesのメディアタイプが"text/html"から"application/json"になり、戻り値がViewableからList<String>を返すように変更しています。JAX-RSはメディアタイプを見て戻り値を自動で適切なデータに変換してくれるためとてもシンプルに処理を記載する事ができます。

web.xmlの追加

JAX-RSのデフォルトではJAXBを使用してJSONデータの変換を行います。しかしJAXBを使用する場合、戻り値のオブジェクトに@XmlRootElementアノテーションをつける必要があるなどなにかとめんどくさい点があります。そこで今回はJSONのパーサにJacksonを使用したいと思います。パーサにJacksonを有効にするにはweb.xmlでinit-paramを指定します。

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
	 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	 version="3.0">
    <servlet>
        <display-name>JAX-RS Servlet</display-name>
        <servlet-name>org.netbeans.rest.application.config.ApplicationConfig</servlet-name>
        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>
    </servlet>
</web-app>


servlet-nameにはjax-rsのパスが定義されているjavax.ws.rs.core.Applicationを継承したクラスのクラス名を記載します。

実行

あとは実行すると前回と同様一覧画面が表示されます。


f:id:den2sn:20121204002935p:plain


サーバでJSONを返すようにする事でサーバの処理と画面の処理をより疎結合に開発する事が出来るようになります。どちらが良いとは言えませんがJSONを返却する今回の方式の方がよりRESTサービス的ではあると言えると思います。


JSF2.0で一覧表示に続く