以前、EL式についての記事を書いたので、OGNL式もついでに書いとけやという事で書きます。
OGNL(Object-Graph Navigation Language)とは、Javaオブジェクトのプロパティにアクセスしたりメソッドを呼び出したりすることの出来るJavaに似た言語であり、主にJSP(Struts2ではstruts.xmlやvaludation.xmlにも)に記述します。
この言語はStruts2(WebWork)やSeasarフレームワークといった有名どころで採用されていますので、これらのフレームワークを使用したJavaの開発では活躍する事と思います。
今回は、Struts2を使用している場合のサンプルコードを書いていきたいと思いますが、OGNL式自体の記述法はSeasarなどでも流用できると思いますので、他のフレームワークを使っている方もご覧になってください。
OGNL式によるJavaオブジェクトの参照
OGNLでJavaオブジェクトを参照する際、ルートオブジェクトとそれ以外のオブジェクトで表現式が分かれます。
ルートオブジェクトであれば、そのオブジェクトの変数名そのままでアクセス出来、それ以外のオブジェクトであればシャープ記号(#)を変数名の頭に付与する事によりアクセス出来ます。
Struts2では、ルートオブジェクトとはバリュースタック(ValueStack)と呼ばれるオブジェクトの事であり、アクションクラス(通常、~Action.javaで実装)は常にバリュースタックに積まれます。
よって、該当アクション処理後のJSP内に記述するOGNL式では、下の例のようにアクションクラスが持っているフィールドには変数名そのままでアクセス出来ます。
//アクションクラス public class SampleAction extends ActionSupport { : private String str = ""; // この変数にJSPからアクセスする : public String getStr() { return str; } public void setStr(String arg) { this.str = arg; } }
<!-- JSP --> <s:property value="str"/> ←このように変数名そのままでアクセスできる
バリュースタック以外(ルート以外)のオブジェクトには以下のように定義済みのものがあり、#を付けることで参照可能です。
application アプリケーションスコープのオブジェクト session セッションスコープのオブジェクト request リクエストスコープのオブジェクト parameters リクエストパラメータ attr page,session,request,applicationのいずれかの最初の要素
例:セッションスコープのオブジェクトへの値設定&参照
//アクションクラス public class SampleAction extends ActionSupport implements SessionAware { : private Map<String, Object> sessionMap; : @Override public void setSession(Map<String, Object> sessionMap) { this.sessionMap = sessionMap; } : public String execute() throws Exception { sessionMap.put("key", "value string"); // ここでセッションマップオブジェクトに"key"という名前のキーで文字列"value string"を格納 } }
<!-- JSP --> <s:property value="#session.key"/> または <s:property value="#session['key']"/>
上記ではアクションクラス内の処理でセッションマップオブジェクトに値を格納し、JSPで参照しています。
セッションオブジェクトは定義済みオブジェクトとして存在しているので、#sessionの記述により参照できます。
リテラル
OGNLではリテラルとしてJavaで利用可能なものがそのまま利用出来ます。
true false null 123 (数値) 'abc' (文字列) "abc" (文字列)
変数①
OGNLでは動的に変数を生成して扱う事が出来ます。
次のサンプルはリクエストスコープのオブジェクトに変数名strで”test”という文字列を代入しています。
<s:set name="" value="#request.str='test'"/> <s:set name="str" value="'test'" scope="request"/>
この変数を参照するには
<s:property value="#request.str"/> または <s:property value="#request.['str']"/> または <s:property value="#attr.str"/> または <s:property value="#attr['str']"/> または <s:property value="#str"/>
と記述します。
変数②(リスト)
OGNLでリストを生成するには{}で囲みます。
<s:set name="list" value="{'value1','value2','value3'}"/>
リスト変数にアクセスするには下記のようにします。
1番目の要素にアクセスする場合
<s:property value="#list[0]"/>
次のようにするとリスト内全てにアクセス出来、’[value1][value2][value3]‘が出力されます。
<s:iterator value="#list"> [<s:property/>] </s:iterator>
変数③(マップ)
OGNLでマップを生成するには#{}で囲みます。
<s:set name="map" value="#{'A':'value1','B':'value2','C':'value3'}"/>
マップオブジェクトへのアクセスは次のようにします。
<s:property value="#map"/> ←'{A=value1, B=value2, C=value3}'が出力される <s:property value="#map['A']"/> ←'value1'が出力される <s:property value="#map.get('A')"/> ←'value1'が出力される <s:iterator value="#map"> ←[A:value1] [B:value2] [C:value3]が出力される [<s:property value="key"/>:<s:property value="value"/>] </s:iterator>
イテレータ内でカレントのマップオブジェクトのアクセスに使用するキーがやや特殊で、
コレクション操作
OGNLではリストやマップといったコレクションに対していくつかの操作を行えます。
コレクション操作の記述
コレクション名.{操作}
下のようなリストを生成した場合
<s:set name="list" value="{'1','22','333'}"/>
list.{操作}
とすることで、各要素について操作を行えます。
コレクション操作中に現在の要素にアクセスするには
#this
と記述します。
コレクション操作で扱える特別な記述子には以下が有ります
? …抽出 ^ …最初に一致する要素の取得 $ …最後に一致する要素の取得
#thisを使用したサンプル↓
<s:set name="list2" value="#list.{ '' + #this + 'a' }"/> <s:iterator value="#list2"> ←'1a 22a 333a'が出力される <s:property/> </s:iterator>
?を使用したサンプル↓
<s:set name="list2" value="#list.{? #this.equals('1') || #this.equals('22') }"/> <s:iterator value="#list2"> ←'1 22'が出力される <s:property/> </s:iterator>
^を使用したサンプル↓
<s:set name="list2" value="#list.{^ #this.equals('1') || #this.equals('22') }"/> <s:iterator value="#list2"> ←'1'が出力される <s:property/> </s:iterator>
$を使用したサンプル↓
<s:set name="list2" value="#list.{$ #this.equals('1') || #this.equals('22') }"/> <s:iterator value="#list2"> ←'22'が出力される <s:property/> </s:iterator>
静的メソッドや定数の参照
次のように”@クラス名@メソッド名”で記述するとJavaの静的なメソッドや定数へのアクセスを行う事ができます。
<s:property value="@java.lang.Math@PI"/> ←3.141592653589793が出力される
使用例
下のコードではマップを生成し、それを元にラジオボタンを生成して画面表示します。
ラジオボタンの選択肢1を選択した場合、regDataというオブジェクトのnumプロパティに1が設定されます。
<s:radio list="#{'1':'選択肢1' , '2':'選択肢2'}" name="regData.num"/>
下のコードでは、SampleAppCommonUtilクラスの静的メソッドgetSampleStringにパラメータとして変数valueAの値を渡した結果を表示します。
<s:property value="@jp.co.sampleapp.common.util.SampleAppCommonUtil@getSampleString(valueA)"/>
下のコードでは、セッションマップオブジェクトに格納されているloginUserキーで取得出来るオブジェクトが持っているregDataというオブジェクトのプロパティuserNicknameを、formDataオブジェクトのfieldNameフィールドに設定するテキストフィールドを生成します。
valueの設定が%{}で囲まれていますが、これを記述しない場合は”#session.loginUser.regData.userNickname”という文字列がformData.fieldNameに設定されてしまいますので必要となります。
<s:textfield name="formData.fieldName" value="%{#session.loginUser.regData.userNickname}"/>
下のコードでは、イテレータの処理内でフラグにより分岐しています。
<s:set name="list" value="{'1','22','333','4444'}"/> <s:iterator value="list" status="st"> <s:if test="2 < #st.count"> <s:set name="flag" value="true"/> </s:if> <s:else> false <s:property/><br/> </s:else> <s:if test="#flag"> true <s:property/><br/> </s:if> </s:iterator>
上記の結果の出力は
false 1 false 22 true 333 true 4444
となります。
このように使い方次第で色々な事が出来ます。