JSTLのCoreタグライブラリ

JSTL(JSP Standard Tag Library)とは、よく需要があるJSPのカスタムタグをSunとJavaコミュニティが汎用化し、 標準と定めたライブラリ(の仕様の集合体)です。 JSTLはCore、XML、SQL、Formatting、Functionsなどに分けられ、 これらの中でも最も基本的なタグライブラリがCoreです。

そして、JSTL仕様のオープンソースの実装として有名なのが、 Jakarta Taglibsです。 Taglibsには、JSTLの各ライブラリの実装のほか、 独自に拡張した20種類以上のタグライブラリが揃っており、便利に利用できます。 ここでは、Taglibsに含まれているCoreタグライブラリを使った例をご紹介します。

Coreタグライブラリのセットアップ手順
以下の説明では、タグライブラリを使いたいWebアプリケーションの、 コンテキストルートのディレクトリを$APP_HOME と表記します。

  1. ホームページ から「Download」ページを辿り、nightly build jakarta-taglibs-YYYYMMDD.zip をダウンロードして、展開します。
  2. jakarta-taglibs/standard/lib ディレクトリのjstl.jar と standard.jar を $APP_HOME/WEB-INF/lib にそれぞれ taglibs-jstl.jar、taglibs-standard.jar という名前でコピーします。
    (※このJARファイルの名前は自由です。)
  3. 最近のtaglibsのCoreタグライブラリでは、 $APP_HOME/WEB-INF/web.xmlにtaglibを記述する必要がなくなりました。 また、同梱の*.tldファイルを、$APP_HOME/WEB-INFにコピーする必要もありません。 JARファイルのMETA-INFフォルダの中にTLDファイルを置いてあるライブラリの場合、 web.xmlへのtaglib記述も、TLDファイルのWEB-INFへの配置も、両方不要になったためです。 ただし、各JSP文書でのURIは「/WEB-INF/c.tld」みたいなURIでは駄目で、 必ず標準のものを使う必要があります。 JSTL 1.0と1.1では標準URIは異なるので注意して下さい。

    1.0用
    <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
    1.1用
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    

サンプル1(Hello, World)

<%@ page language="java" contentType="text/html;charset=Shift_JIS" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<html><body>
<font color="#700040">●Jakarta Taglibs JSTL Core(c)のテスト</font><br>

<c:set var="hello1" value="Hello, World" />
<c:set var="hello2">
<font color="red">Hello, World(red)</font>
</c:set>

<c:out value="${hello1}"/><br>
<c:out value="${hello2}" escapeXml="false"/><br>

</body></html>

最も基本的なc:setタグとc:outタグの使用例です。 c:setタグはvar=で指定した変数に値をセットします。 ここでは書いていませんが、Beanオブジェクトにプロパティ値をセットすることもできます。 c:outタグはvalue=で指定した値をJSPストリームに出力します。 ${変数名}とすると変数の参照ができます。 この、中括弧{}は省略できないので注意してください。 省略して「$hello1」と書くと、そのまま「$hello1」が表示されることになります。

また、escapeXml = "false" にすると、値の中にHTMLタグが含まれていると、それを展開せずにそのまま出力します (つまり、HTMLタグが効果を表します)。 escapeXml = "true" とすると、HTMLタグは自動的にエスケープされます (こちらがデフォルト)。

サンプル2(暗黙オブジェクトの使用)

<%@ page language="java" contentType="text/html;charset=Shift_JIS" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<html><body>
<font color="#700040">●Jakarta Taglibs JSTL Core(c)のテスト</font><br>

<c:choose>
<c:when test="${param.username == null}">
<form action="<c:out value="${pageContext.request.requestURI}" />" method="post">
名前を入力して下さい。
<input type="text" name="username"></input><br>
<input type="submit" value="OK"></input>
</form>
</c:when>
<c:when test="${param.username == ''}">
おおっと。名前を入力して下さい。
<a href="<c:out value="${pageContext.request.requestURI}" />">
戻る
</a>
</c:when>
<c:otherwise>
こんにちは、<font color="red"><c:out value="${param.username}"/></font>さん。
</c:otherwise>
</c:choose>

</body></html>

JSPにもout、request、responseなど、 最初から使える暗黙オブジェクトが幾つかありますが、 JSTLでも同様に、フォーム変数を表すparam、 サーブレットではgetPageContext()で取得できるpageContext、 HTTPヘッダを表すheaderなど幾つかの暗黙オブジェクトがサポートされています。 これらを使うと、記述を大幅に簡略化できます。 例えば、フォーム変数usernameの値は${param.username}で参照できます。 このJSPページのURIは${pageContext.request.requestURI}で参照できます(※1)。
(※1) 後述のように、JSPの暗黙オブジェクトの一覧と、JSTLの暗黙オブジェクトのそれは同じものではありません。 JSTLには、request、sessionなどの暗黙オブジェクトはありません。 代わりに、「pageContext.request」のような式で設定する必要があります。

CoreタグライブラリにはifやforEachのような制御構造を実装したタグも幾つかあります。 このサンプルではc:choosec:whenc:otherwise のタグを使って、CやJavaでいうswitch文のような複数条件分岐を行っています。 この他に、c:ifというタグもありますが、 XSLTのxsl:ifと同じくelseがないので、 一方向の条件分岐の場合にのみ使うことになるでしょう。 なお、これらのタグの条件式を指定する属性は、 XSLTと同じくtestです。 一部selectと書いているドキュメントもありますが、typoのようです。

サンプル3(リダイレクトと画面渡りの例)

<%@ page language="java" contentType="text/html;charset=Shift_JIS" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<html><body>
<font color="#700040">●Jakarta Taglibs JSTL Core(c)のテスト</font><br>

<h2>暗黙オブジェクトparamとheaderのテスト</h2>

<c:choose>
<c:when test="${param.username == null}">
<form action="<c:out value="${pageContext.request.requestURI}" />" method="post">
名前を入力して下さい。
<input type="text" name="username"></input><br>
<input type="submit" value="OK"></input>
</form>
</c:when>
<c:when test="${param.username == ''}">
おおっと。名前を入力して下さい。
<a href="<c:out value="${pageContext.request.requestURI}" />">
戻る
</a>
</c:when>
<c:otherwise>
<!--
 c:redirectタグはパラメータをGETメソッドで渡すのに対し、
 jsp:forwardはPOSTで渡します。
-->
<jsp:forward page="redirect2.jsp">
  <jsp:param name="username" value="<%= request.getParameter("username") %>" />
</jsp:forward>
</c:otherwise>
</c:choose>

</body></html>

<%@ page language="java" contentType="text/html;charset=Shift_JIS" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<html><body>

アプリケーションメインページ

<%
String referer = request.getHeader("Referer");
String username = request.getParameter("username");
%>

チェック情報:
[<%= referer %>] [<%= username %>] <br>

<%
if(referer == null || username == null ||
! referer.equals("http://localhost:8080/app/redirect1.jsp")){
%>
  <font color="red">ログインしていません。</font>
<%
}
%>
</body></html>

上のサンプル2を改造して、最初のページ(redirect1.jsp)でユーザ名が入力されると、 2つめのページ(redirect2.jsp)からメインのアプリケーションが始まる、 という感じにしています。 redirect1.jspからredirect2.jspにジャンプさせるのに、 ここではjsp:forwardタグを使っています。 Coreタグライブラリの中には c:redirectタグという、同様の機能をもつタグもあるのですが、 c:redirectタグを使うと、パラメータをGET方式で、 つまりURLの後ろに?パラメータ名=値、の形でつけてしまうので、 パラメータの内容が予測されてしまうという欠点があります。 jsp:forwardタグでパラメータを(jsp:paramで)指定した場合には、 パラメータがURLに含まれることはありませんので、こちらを使っています。


Coreタグの変数をJSPコードで加工するには

Coreタグライブラリのc:setタグで使用する変数の正体は、 JSPのページコンテキストに記憶される属性変数です。そのため、JSPのコード・ブロックから、 pageContext.getAttributeメソッドと pageContext.setAttributeメソッドを使ってアクセスできます。
次の例はこの典型的な使い方のひとつです。 通常Webフォームなどでセットし渡されるリクエスト・パラメータはCoreタグライブラリの式(EL)の中で ${param.パラメータ名} で参照できますが、 パラメータのエンコーディングを変換する必要がある場合に、 その変換をJSPコード・ブロックで行い、ページコンテキストの属性にセットしています。

<%
String param = request.getParameter("word");
param = new String(param.getBytes("8859_1"), "UTF-8");
pageContext.setAttribute("word", param);
%>
パラメータ:<c:out value="${word}"/><br/>


その他のメモ

c:forEachタグのテンプレート

<c:forEach var="e" items="${mapList}" varStatus="loop">
  <tr>
  <td>${loop.count}</td>
  <td><html:checkbox property="selectedMap(${e['shitenCode']})" value="${e['shitenCode']}" /></td>
  <td>${e["shitenCode"]}</td><td>${e.shitenName}</td><td>${e['address']}</td>
  </tr>
</c:forEach>

c:forEachタグには上記のほかに、begin、end、stepの属性もあり、 それぞれループ開始要素のインデックス、ループ終了要素のインデックス、増分です。
またvarStatusで指定した変数には、ループに関する情報が自動的に設定され、 上の例のようにEL式で参照できます。
current現在処理対象の要素
index0から始まる連番
count1から始まる連番
first最初の要素なら真(boolean)
last最後の要素なら真(boolean)
beginbegin属性の値=ループカウンタの初期値
endend属性の値=ループカウンタの最後の値
stepstep属性の値=ループカウンタの増分
値0から9までループする例は以下のようになります。
変数loop.indexには0から9の値が毎回設定されるので、 +1すると1から10の値が表示されることになります。

<c:forEach begin="0" end="9" step="1" varStatus="loop">
<tr>
  <td align="right">${loop.index + 1}</td>
  <td>******</td>
</tr>
</c:forEach>

c:forEachタグのリストを文字列で書く場合の注意
c:forEachタグのリスト(コレクション)を書くとき、 ListやIteratorオブジェクトを指定するか、またはカンマで区切った文字列を指定できますが、 この文字列を指定するとき、 カンマの前後に空白文字を入れるとその空白文字もループ変数の値に含まれます。 例えば、

<c:forEach var="index" items="01, 02, 03, 04, 05">

のように書くと、「01」だけは前後に空白が入りませんが、02から05までは「0」 の前に空白文字が1個入ってしまい、おそらく期待した処理が出来ないでしょう。

request属性、session属性、application属性の参照
HttpServletRequest.setAttribute()、 HttpSession.setAttribute()、ServletContext.setAttribute() でそれぞれセットできる各スコープの属性は、Coreタグの式内で次のように簡単に参照できます。

<c:out value="${requestScope.shiten}" />
<c:out value="${sessionScope.shiten}" />
<c:out value="${applicationScope.shiten}" />

「request.」や「session.」ではなくて「requestScope.」なので注意してください。 うっかりしていると、忘れてしまうのですよ。 なお、JSP 2.0の今では、それらはそれぞれただ「${shiten}」とだけ書けばOKです。

c:forEachタグでリクエスト・パラメータ(param変数)を参照する
リクエスト・パラメータ(param変数) の一覧を取得するという処理は汎用的なツールを使うときにしか必要にならないためか、 あまりワールド・ワイド・ウェブ上には例が載っていないですが、 一応次のようにすると可能でした。

<c:forEach var="e" items="${param}">
  ${e.key} | ${e.value}<br/>
</c:forEach>

リストの要素数(size)を参照するには?
JSTLになくて困る機能の筆頭が、オブジェクトがListの場合に、 その要素の数を参照する演算子がないことです。 Listのsize()メソッドがgetなにがしというJavaBeansの命名通りでないためですが、 これはJSTL 1.1で設けられたFunctionsライブラリ(EL関数)の機能を使って、次のようにすれば解決できます。

<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
...(中略)
${fn:length(TableForm.recordList)}行のデータが見つかりました。<br/>

ここでは、session.getAttribute("TableForm").getRecordList()が返す値がListのサブクラスです。

EL関数の一覧
JSTL標準FunctionsでサポートされているEL関数のリストは、 こちらです。ホントに基本的な文字列処理ばかりですね。 少し凝った処理をしたくなったらどうしたらよいんでしょう。まったく、もー

EL関数の自作
と、というわけで…
上記のfn:lengthのように、式言語(EL)の中で使える関数を自作することも可能です。 それは、Javaのstaticメソッドとして実装します。 AtmarkITのこのページ に実装例が載っています。なお、TLDに書くメソッドのシグネチャは、 間違えないように書かないといけません。 一致しないと呼び出してくれず、(普通の設定だと多分) サーブレットコンテナのログにも出力されないので注意して下さい。

EL式でrequestオブジェクトは参照できる?
参照できますが、JSPとは異なり、 EL式にはrequestやsession等の暗黙変数はありません。 (requestScopeやsessionScope、pageContext等の変数はある) 従って、EL式の中で「request」と書いても効きません (エラーにはなりませんが、nullで初期化された新しい変数とみなされ、 期待通りの動きはしない)。正解は、「pageContext.request」です。 PageContextクラスにはgetRequest()メソッドがあるので、 上記の記法で取り出せるのです。

EL式で変数同士の数値比較をするには?

<c:if test="${value > 10}">

とすると、右辺が数値のためvalueの値が数値に変換され、 数値同士の比較が行われます。では変数同士の場合は?
value1が"6"、value2が"10"としますと

<c:if test="${value1 gt value2}">

この式の値は真("6"の方が大きい)という結果になってしまいます。 文字列として比べるとASCIIコード順なので6が1より大きいためです。
では数値として変数同士を比較するには?答えは

<c:if test="${value1 gt 0+value2}">

片方が数値だと数値に寄せて比較されます。 ELでは、オペランドの片方が数値だと、文字列が数値に変換されるという特性があります。 ELの演算子の性質については、 (株)ヴィザーズのJava Sticky Noteのこのページ に詳しい説明があります。 なお、これは全てのプログラミング言語にはあてはまりません。 別のページに書いていますが、JavaScriptは正反対で、 数値が文字列に合わされるという性質があります。

このエラーはなんぞ?

javax.servlet.jsp.JspTagException: <forEach> 内で供給された "items" を反復処理する方法が不明です
これは、c:forEachタグのitems属性で指定した値(requestスコープなどの属性名)が参照するオブジェクトが、 コレクション型(Listや配列)ではないクラスのインスタンスのため、 c:forEachタグで処理できない、という意味です。

Server Side Java Index Top

(first uploaded 2003/02/23 last updated 2009/10/26, URANO398)

Gポイントポイ活 Amazon Yahoo 楽天

無料ホームページ 楽天モバイル[UNLIMITが今なら1円] 海外格安航空券 海外旅行保険が無料!